How to get the upper line of a polygon

I am trying to create an application to automate the workflow of dam analysis for the geotechnical sector I work in. The app takes as input a DXF file that contains the geometry of the dam section. From this file, we extract data such as points, edges, and so on. One of the requirements for the app to work is that the section must be a closed polygon (see images in the example for reference).

Now, I need to find the upper line that “covers” this polygon (the first two images show what this line should be for each example section) and a base line that starts from the leftmost point and goes to the rightmost point. The intention is to have the dam polygon covered by both lines, essentially creating a “sandwich” with these lines to determine its geometry.

I attempted to create this line using my own logic, but it was not successful. I now need a method to find this upper line that covers all points on the top (and only on the top) of the polygon. I searched for algorithms online, but all I could find were algorithms to determine if a point is inside a polygon (such as the convex hull), which do not help me.

Below is the code I have been using to determine this top_line and base_line (which is incorrect). Does anyone know how to solve this or if there are any libraries that could help? I have tried Shapely, but with no success. Thanks in advance, any help is much appreciated!




import matplotlib.pyplot as plt
from shapely.geometry import LineString


# Dados das arestas
edges = [
    ((-20.829, 55.53), (-20.829, 53.13)),
    ((-20.829, 55.53), (34.552, 55.53)),
    ((-20.829, 55.53), (-20.829, 58.63)),
    ((-20.829, 53.13), (34.552, 53.13)),
    ((-20.829, 53.13), (-20.829, 49.73)),
    ((34.552, 53.13), (34.552, 55.53)),
    ((34.552, 53.13), (34.552, 49.73)),
    ((34.552, 55.53), (34.552, 57.376)),
    ((-14.727, 59.75), (-18.158, 59.75)),
    ((-14.727, 59.75), (-14.727, 64.737)),
    ((-14.727, 59.75), (-8.625, 59.75)),
    ((-18.158, 59.75), (-18.158, 64.737)),
    ((-18.158, 59.75), (-20.829, 59.75)),
    ((-18.158, 64.737), (-14.727, 64.737)),
    ((-2.25, 64.0), (-8.625, 59.75)),
    ((-2.25, 64.0), (-1.25, 64.0)),
    ((-8.625, 59.75), (-7.625, 59.75)),
    ((-7.625, 59.75), (-1.25, 64.0)),
    ((-7.625, 59.75), (-7.125, 59.75)),
    ((-1.25, 64.0), (-0.75, 64.0)),
    ((-7.125, 59.75), (-0.75, 64.0)),
    ((-7.125, 59.75), (7.125, 59.75)),
    ((-0.75, 64.0), (0.75, 64.0)),
    ((7.125, 59.75), (7.625, 59.75)),
    ((7.125, 59.75), (0.75, 64.0)),
    ((7.625, 59.75), (8.625, 59.75)),
    ((7.625, 59.75), (1.25, 64.0)),
    ((8.625, 59.75), (10.951, 59.75)),
    ((8.625, 59.75), (2.25, 64.0)),
    ((10.951, 59.75), (11.613, 59.487)),
    ((11.613, 59.487), (13.195, 59.11)),
    ((13.195, 59.11), (13.747, 58.75)),
    ((13.747, 58.75), (13.75482973669868, 58.745690645800494)),
    ((13.75482973669868, 58.745690645800494), (13.965006969842744, 58.6300126636222)),
    ((13.75482973669868, 58.745690645800494), (16.755, 58.746)),
    ((13.965006969842744, 58.6300126636222), (-20.829, 58.63)),
    ((13.965006969842744, 58.6300126636222), (15.444, 57.816)),
    ((-20.829, 58.63), (-20.829, 59.75)),
    ((0.75, 64.0), (1.25, 64.0)),
    ((1.25, 64.0), (2.25, 64.0)),
    ((15.586, 57.75), (15.444, 57.816)),
    ((15.586, 57.75), (16.034, 57.516)),
    ((34.552, 57.376), (34.174, 57.271)),
    ((34.174, 57.271), (33.816, 57.194)),
    ((33.816, 57.194), (31.833, 56.75)),
    ((31.833, 56.75), (31.289, 56.716)),
    ((31.289, 56.716), (30.511, 56.695)),
    ((30.511, 56.695), (30.095, 56.693)),
    ((30.095, 56.693), (29.607, 56.699)),
    ((29.607, 56.699), (29.104, 56.718)),
    ((29.104, 56.718), (28.437, 56.75)),
    ((28.437, 56.75), (28.064018072840494, 56.76349934588915)),
    ((28.064018072840494, 56.76349934588915), (27.691, 56.777)),
    ((28.064018072840494, 56.76349934588915), (26.505, 58.652)),
    ((27.691, 56.777), (24.817032481790534, 56.964497880885276)),
    ((24.817032481790534, 56.964497880885276), (21.943, 57.152)),
    ((24.817032481790534, 56.964497880885276), (26.505, 58.652)),
    ((21.943, 57.152), (20.025, 57.067)),
    ((20.025, 57.067), (19.16600997989089, 57.16608651658138)),
    ((19.16600997989089, 57.16608651658138), (17.641, 57.342)),
    ((19.16600997989089, 57.16608651658138), (16.755, 58.746)),
    ((17.641, 57.342), (16.254, 57.447)),
    ((16.254, 57.447), (16.034, 57.516)),
    ((34.552, 37.864), (-20.829, 37.864)),
    ((34.552, 37.864), (34.552, 40.875)),
    ((-20.829, 37.864), (-20.829, 40.875)),
    ((-20.829, 40.875), (34.552, 40.875)),
    ((-20.829, 40.875), (-20.829, 46.59)),
    ((34.552, 40.875), (34.552, 46.59)),
    ((34.552, 46.59), (-20.829, 46.59)),
    ((34.552, 46.59), (34.552, 49.73)),
    ((-20.829, 46.59), (-20.829, 49.73)),
    ((34.552, 49.73), (-20.829, 49.73))
]

# Função para encontrar o ponto mais à esquerda com maior Y
def find_start_point(edges):
    start_point = None
    for edge in edges:
        for point in edge:
            if start_point is None or (point[0] < start_point[0]) or (point[0] == start_point[0] and point[1] > start_point[1]):
                start_point = point
    print("Start Point:", start_point)
    return start_point

def find_end_point(edges):
    end_point = None
    for edge in edges:
        for point in edge:
            if end_point is None or (point[0] > end_point[0]) or (point[0] == end_point[0] and point[1] > end_point[1]):
                end_point = point

    print("End Point:", end_point)
    return end_point


# Função para encontrar o próximo ponto baseado nas arestas conectadas
def find_next_point(current_point, edges, visited_points):
    connected_points = []
    for edge in edges:
        if edge[0] == current_point:
            connected_points.append(edge[1])
        elif edge[1] == current_point:
            connected_points.append(edge[0])

    if not connected_points:
        return None

    # Filtrar os pontos conectados para remover os pontos já visitados
    connected_points = [p for p in connected_points if p not in visited_points]
    if not connected_points:
        return None

    # Verifica se há pontos com Y maior que o do ponto atual
    has_higher_y = any(p[1] > current_point[1] for p in connected_points)
    last_visited_point = visited_points[-2] if len(visited_points) > 1 else current_point

    print("Current Point:", current_point)
    print("Connected Points:", connected_points)
    print("Has Higher Y:", has_higher_y)
    print("Last Visited Point:", last_visited_point)

    if has_higher_y:
        # Priorizar pontos com maior Y e, em caso de empate, menor X
        potential_next_point = max(connected_points, key=lambda p: (p[1], -p[0]))
        print("Potential Next Point (Higher Y):", potential_next_point)
        if potential_next_point[1] > last_visited_point[1]:
            print("Last Visited Point used in comparison", last_visited_point)
            next_point = potential_next_point
        else:
            next_point = max(connected_points, key=lambda p: (p[0], p[1]))
    else:
        # Priorizar pontos com maior Y e maior X
        next_point = max(connected_points, key=lambda p: (p[1], p[0]))

    print("Chosen Next Point:", next_point)
    print("-" * 40)

    return next_point


# Função principal para encontrar a linha de topo
def find_top_line(edges):
    top_line = []
    visited_points = []
    start_point = find_start_point(edges)
    end_point = find_end_point(edges)
    top_line.append(start_point)
    visited_points.append(start_point)  # Adicionar o ponto inicial à lista de pontos visitados
    current_point = start_point

    while True:
        next_point = find_next_point(current_point, edges, visited_points)
        if next_point is None or next_point == start_point or next_point == end_point:
            break
        top_line.append(next_point)
        visited_points.append(next_point)  # Adicionar o próximo ponto aos pontos visitados aqui
        current_point = next_point
        print("Top Line So Far:", top_line)
        print("Visited Points So Far:", visited_points)
        print("=" * 40)

    # Adicionar o ponto final à linha de topo
    if next_point == end_point:
        top_line.append(end_point)

    return top_line


# Função para encontrar o próximo ponto baseado nas arestas conectadas para a linha de fundação
def find_next_point_base(current_point, edges, visited_points):
    connected_points = []
    for edge in edges:
        if edge[0] == current_point:
            connected_points.append(edge[1])
        elif edge[1] == current_point:
            connected_points.append(edge[0])

    if not connected_points:
        return None

    # Se houver mais de um ponto conectado, priorize mover para a direita
    if len(connected_points) > 1:
        connected_points = [p for p in connected_points if p not in visited_points and p[0] > current_point[0]]

    next_point = None
    for point in connected_points:
        if point in visited_points:
            continue
        if next_point is None:
            next_point = point
        elif point[1] < next_point[1]:
            next_point = point
        elif point[1] == next_point[1] and point[0] > next_point[0]:
            next_point = point

    return next_point

# Função principal para encontrar a linha de fundação
def find_base_line(edges):
    base_line = []
    visited_points = set()
    start_point = find_start_point(edges)
    end_point = find_end_point(edges)
    base_line.append(start_point)
    visited_points.add(start_point)
    current_point = start_point

    while True:
        next_point = find_next_point_base(current_point, edges, visited_points)
        if next_point is None or next_point == start_point or next_point == end_point:
            break
        base_line.append(next_point)
        visited_points.add(next_point)
        current_point = next_point

    # Adicionar o ponto final à linha de fundação
    if next_point == end_point:
        base_line.append(end_point)

    return base_line


# Encontrar a linha de topo e a linha de fundação
top_line = find_top_line(edges)

base_line = find_base_line(edges)


# Função para plotar todas as arestas, a linha de topo e a linha de base
def plot_edges_and_lines(edges, top_line, base_line):
    plt.figure(figsize=(10, 8))

    # Plotar todas as arestas
    for edge in edges:
        x_values = [edge[0][0], edge[1][0]]
        y_values = [edge[0][1], edge[1][1]]
        plt.plot(x_values, y_values, 'b-', linewidth=1)

    # Plotar a linha de topo
    top_x = [point[0] for point in top_line]
    top_y = [point[1] for point in top_line]
    plt.plot(top_x, top_y, 'r-', linewidth=2, label='Linha de Topo')

    # Plotar a linha de fundação
    base_x = [point[0] for point in base_line]
    base_y = [point[1] for point in base_line]
    plt.plot(base_x, base_y, 'g-', linewidth=2, label='Linha de Fundação')

    # Configurar o gráfico
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.title('Arestas, Linha de Topo e Linha de Fundação')
    plt.legend()
    plt.grid(True)
    plt.show()

# Plotar todas as arestas e as linhas de topo e fundação
plot_edges_and_lines(edges, top_line, base_line)

I have tried creating my own logic, explicitly specifying which points the line should pass through. This approach worked for the first image in the reference but not for the second. From this, I realized that the logic will vary greatly depending on the polygon.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật