본문 바로가기

Python

평면에서 일정 영역 진입/진출 확인하기

2D 평면 상에서 일정 영역에 진입/진출 시점을 확인해보자. 

 

예를 들어, 어떤 도시가 있고 그 도시 내의 도로를 자동차들이 지나간다고 하자. 자동차들의 위치는 GPS 등을 통해 매 1초마다 알 수 있다고 하자. 이런 상황에서 도시 내의 어떤 지역에 자동차가 들어온 시간과 나간 시간을 알고 싶다고 하자. 

이것을 어떻게 구할 수 있을까? 

 

흠.. 일정 영역에 진입했다, 진출했다를 계산하기가 생각보다 간단하지가 않다. 

그러면 쳇지피티에게 일단 물어보자. 

역시 너구나~ 믿고 있었다구~

쳇지피티가 알려준 코드는 아래와 같다. 

import numpy as np
import matplotlib.pyplot as plt

def generate_path(steps=100, bounds=(-10, 10)):
    """ 이동 객체의 경로를 생성 """
    x = np.cumsum(np.random.randint(-1, 2, size=steps))
    y = np.cumsum(np.random.randint(-1, 2, size=steps))
    return np.column_stack((x, y))

def find_entry_exit(path, region):
    """ 특정 구간 진입 및 이탈 시간 계산 """
    entry_time, exit_time = None, None
    for i, (x, y) in enumerate(path):
        if region[0] <= x <= region[1] and region[2] <= y <= region[3]:
            if entry_time is None:
                entry_time = i
            exit_time = i
    return entry_time, exit_time

def plot_path(path, region, entry_time, exit_time):
    """ 이동 경로 시각화 """
    plt.figure(figsize=(8, 8))
    plt.plot(path[:, 0], path[:, 1], '-o', markersize=2, label='Path')
    
    # 영역 그리기
    rect = plt.Rectangle((region[0], region[2]), region[1]-region[0], region[3]-region[2],
                         linewidth=2, edgecolor='r', facecolor='none', label='Target Region')
    plt.gca().add_patch(rect)
    
    # 진입/이탈 위치 표시
    if entry_time is not None:
        plt.scatter(path[entry_time, 0], path[entry_time, 1], color='green', s=100, label='Entry')
    if exit_time is not None:
        plt.scatter(path[exit_time, 0], path[exit_time, 1], color='red', s=100, label='Exit')
    
    plt.xlabel('X Position')
    plt.ylabel('Y Position')
    plt.legend()
    plt.title('Object Path and Region Entry/Exit')
    plt.grid()
    plt.show()

# 이동 경로 생성
path = generate_path(200)
region = (-3, 3, -3, 3)  # 특정 구간 (사각형)

# 진입 및 이탈 시간 계산
entry_time, exit_time = find_entry_exit(path, region)

distance_traveled = np.sum(np.linalg.norm(np.diff(path[entry_time:exit_time+1], axis=0), axis=1)) if entry_time is not None and exit_time is not None else 0

# 결과 출력
print(f"Entry Time: {entry_time} seconds")
print(f"Exit Time: {exit_time} seconds")
print(f"Time Spent: {exit_time - entry_time if entry_time is not None and exit_time is not None else 0} seconds")
print(f"Distance Traveled: {distance_traveled:.2f} units")

# 이동 경로 시각화
plot_path(path, region, entry_time, exit_time)

 

야.. 잠깐만.. 좀 이상한데? 

일단 코드를 실행해보면 아래와 같은 결과가 나온다. 

흠.. 뭔가 좀 이상한데?

쳇지피티가 알려준 코드를 좀 자세히 들여다보자. 

# 이동 경로 생성
path = generate_path(200)
region = (-3, 3, -3, 3)  # 특정 구간 (사각형)

이 부분을 보면 generate_path() 함수에서 이동객체의 경로를 랜덤하게 생성하도록 되어 있고, 

region 설정도 4개의 숫자로 설정하게 되어 있다. 이 4개의 숫자는 좌측하단 x,y 좌표와 우측상단 x,y 좌표를 의미하는 것 같다. 

즉, 어떤 지역을 설정할 때 x, y 축에 평행한 직사각형으로만 지역을 설정할 수 있게 했다. 

 

씁.. 이걸 원하는게 아닌데.. 

 

좀 더 수정을 해보자. 

일단 이동객체의 경로를 직선으로 생성하도록 하자. 그리고 region 설정도 4개의 숫자가 아닌, 4개의 point(8개의 숫자)로 설정할 수 있게 변경하자. 

좋아, 경로도 직선으로 되었고, 구간 설정도 4개의 point로 할 수 있게 되었다.

 

다시 알려준 코드는 아래와 같다. 

import numpy as np
import matplotlib.pyplot as plt

def generate_path(steps=100, start=(-10, -10), end=(10, 10)):
    """ 이동 객체의 직선 경로 생성 """
    x = np.linspace(start[0], end[0], steps)
    y = np.linspace(start[1], end[1], steps)
    return np.column_stack((x, y))

def is_inside_region(x, y, region):
    """ 점이 사각형 영역 안에 있는지 확인 (네 꼭짓점 사용) """
    x1, y1, x2, y2, x3, y3, x4, y4 = region
    min_x, max_x = min(x1, x2, x3, x4), max(x1, x2, x3, x4)
    min_y, max_y = min(y1, y2, y3, y4), max(y1, y2, y3, y4)
    return min_x <= x <= max_x and min_y <= y <= max_y

def find_entry_exit(path, region):
    """ 특정 구간 진입 및 이탈 시간 계산 """
    entry_time, exit_time = None, None
    for i, (x, y) in enumerate(path):
        if is_inside_region(x, y, region):
            if entry_time is None:
                entry_time = i
            exit_time = i
    return entry_time, exit_time

def plot_path(path, region, entry_time, exit_time):
    """ 이동 경로 시각화 """
    plt.figure(figsize=(8, 8))
    plt.plot(path[:, 0], path[:, 1], '-o', markersize=2, label='Path')
    
    # 영역 그리기 (다각형)
    polygon = np.array(region).reshape(4, 2)
    plt.fill(*zip(*polygon), edgecolor='r', fill=False, linewidth=2, label='Target Region')
    
    # 진입/이탈 위치 표시
    if entry_time is not None:
        plt.scatter(path[entry_time, 0], path[entry_time, 1], color='green', s=100, label='Entry')
    if exit_time is not None:
        plt.scatter(path[exit_time, 0], path[exit_time, 1], color='red', s=100, label='Exit')
    
    plt.xlabel('X Position')
    plt.ylabel('Y Position')
    plt.legend()
    plt.title('Object Path and Region Entry/Exit')
    plt.grid()
    plt.show()

# 이동 경로 생성
path = generate_path(200, start=(-10, -10), end=(10, 10))
region = (-3, -3, 3, -3, 3, 3, -3, 3)  # 특정 구간 (4점 좌표)

# 진입 및 이탈 시간 계산
entry_time, exit_time = find_entry_exit(path, region)

distance_traveled = np.sum(np.linalg.norm(np.diff(path[entry_time:exit_time+1], axis=0), axis=1)) if entry_time is not None and exit_time is not None else 0

# 결과 출력
print(f"Entry Time: {entry_time} seconds")
print(f"Exit Time: {exit_time} seconds")
print(f"Time Spent: {exit_time - entry_time if entry_time is not None and exit_time is not None else 0} seconds")
print(f"Distance Traveled: {distance_traveled:.2f} units")

# 이동 경로 시각화
plot_path(path, region, entry_time, exit_time)

 

실행 결과는 아래와 같다. 

오, 좋아. 원하는 대로 잘 만들어 준것 같은데?

오, 결과가 깔끔하다. 

역시 지피티!! 믿을만... 잠깐... 살짝 코드가 이상한데? 밑장 빼기냐? 

 

정작 중요한 영역 내부 판단 코드가... 단순 min, max로 구현되어 있다? 

def is_inside_region(x, y, region):
    """ 점이 사각형 영역 안에 있는지 확인 (네 꼭짓점 사용) """
    x1, y1, x2, y2, x3, y3, x4, y4 = region
    min_x, max_x = min(x1, x2, x3, x4), max(x1, x2, x3, x4)
    min_y, max_y = min(y1, y2, y3, y4), max(y1, y2, y3, y4)
    return min_x <= x <= max_x and min_y <= y <= max_y

 

 

이거 이상한데?

만약 4점의 x, y 의 min, max 가 서로 다를 때는 어떻게 되지? 

한번 확인해보자. 

이봐이봐. 내가 빙다리 핫바지로 보이냐?

 

지정한 영역의 밖에서 Entry 했다고 체크되었고, 영역 밖으로 나간 이후에 더 있다가 Exit 했다고 체크되었다. 

이게 아니야! 다시 해줘!

 

이제야 사실을 말하는 군.

 

그림과 함께 잘못된 점을 얘기하니, 그제야 잘못된 점을 실토한다. 

그러면서 새로운 코드를 알려주었다. 

import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import Point, Polygon

def generate_path(steps=100, start=(-10, -10), end=(10, 10)):
    """ 이동 객체의 직선 경로 생성 """
    x = np.linspace(start[0], end[0], steps)
    y = np.linspace(start[1], end[1], steps)
    return np.column_stack((x, y))

def is_inside_region(x, y, region):
    """ 점이 다각형 영역 안에 있는지 확인 (Shapely 사용) """
    polygon = Polygon(np.array(region).reshape(4, 2))
    return polygon.contains(Point(x, y))

def find_entry_exit(path, region):
    """ 특정 구간 진입 및 이탈 시간 계산 """
    entry_time, exit_time = None, None
    for i, (x, y) in enumerate(path):
        if is_inside_region(x, y, region):
            if entry_time is None:
                entry_time = i
            exit_time = i
    return entry_time, exit_time

def plot_path(path, region, entry_time, exit_time):
    """ 이동 경로 시각화 """
    plt.figure(figsize=(8, 8))
    plt.plot(path[:, 0], path[:, 1], '-o', markersize=2, label='Path')
    
    # 영역 그리기 (다각형)
    polygon = np.array(region).reshape(4, 2)
    plt.fill(*zip(*polygon), edgecolor='r', fill=False, linewidth=2, label='Target Region')
    
    # 진입/이탈 위치 표시
    if entry_time is not None:
        plt.scatter(path[entry_time, 0], path[entry_time, 1], color='green', s=100, label='Entry')
    if exit_time is not None:
        plt.scatter(path[exit_time, 0], path[exit_time, 1], color='red', s=100, label='Exit')
    
    plt.xlabel('X Position')
    plt.ylabel('Y Position')
    plt.legend()
    plt.title('Object Path and Region Entry/Exit')
    plt.grid()
    plt.show()

# 이동 경로 생성
path = generate_path(200, start=(-10, -10), end=(10, 10))
region = [(-4, -3), (2, -4), (3, 3), (-3, 4)]  # 기울어진 사각형 (4점 좌표)

# 진입 및 이탈 시간 계산
entry_time, exit_time = find_entry_exit(path, region)

distance_traveled = np.sum(np.linalg.norm(np.diff(path[entry_time:exit_time+1], axis=0), axis=1)) if entry_time is not None and exit_time is not None else 0

# 결과 출력
print(f"Entry Time: {entry_time} seconds")
print(f"Exit Time: {exit_time} seconds")
print(f"Time Spent: {exit_time - entry_time if entry_time is not None and exit_time is not None else 0} seconds")
print(f"Distance Traveled: {distance_traveled:.2f} units")

# 이동 경로 시각화
plot_path(path, region, entry_time, exit_time)

 

위의 코드를 실행하려고 하니 shapely 모듈을 찾을 수 없다고 에러가 난다. 

흠. 찾을 수 없다는데?

모듈을 찾을 수 없다면, 설치해주자. 

shapely 모듈은 아래 명령어로 쉽게 설치가 가능하다. 

pip install shapely

 

그리고 위의 코드에서 좀 더 기울어진 사각형 영역을 설정하기 위해 아래처럼 region 을 수정하였다. 

region = [(-4, -3), (2, -4), (0, 3), (-3, 4)]  # 기울어진 사각형 (4점 좌표)

 

실행 결과는 아래와 같다. 

오, 원하는 대로 잘 나왔다.

오, 이제야 원하는 대로 잘 나왔다. 

사각형 영역이 삐뚤어져도 그 진입지점과 진출지점을 정확하게 검출해냈다. 

 

지피티, 고생했어. 

 

오늘은 여기까지!