CODEKILLER

알림 : [namoosystem.com] Codekiller 나무의사 빨간펜 인강, 재해위험성 검토의견서 QGIS 강의

반응형

재해위험성 검토의견서 SharkGeo

WPF GIS에서 DXF 맵 마우스 오버 하이라이트 구현하기

WPF 기반 GIS 애플리케이션(SharkGeo)에서 DXF 도면 위에 마우스를 올리면 해당 엔티티가 골드 글로우로 하이라이트되고, 레이어 필터 패널·속성 테이블까지 3방향 양방향 연동되는 인터랙티브 호버 시스템을 구현했습니다.

 

1. 문제 정의

DXF 도면은 수만 개의 엔티티(폴리라인, 선분, 원 등)로 구성됩니다. 사용자가 마우스를 움직일 때마다 "어떤 엔티티 위에 있는가"를 실시간으로 판별해야 하는데, 이때 두 가지 핵심 과제가 있습니다:

  1. 성능: 수만 개 엔티티에 대한 히트 테스트를 매 프레임 수행하면 UI가 멈춤
  2. 렌더링 비용: 하이라이트를 위해 전체 맵을 매번 다시 그리면 너무 느림

DXF 맵에서만 마우스 Over Hit 검증만 하더라도 Object가 많아서 느려질 수 있는 부분인데, 속성테이블(하단), 속성창(우측)의 아이템과 엑티브효과를 같이 주어야 합니다. 기본 전체 엔티티가 올라가야 하는 구조와, 골드색상으로 하이라이트 되는 레이어를 구조를 만들어 줍니다. 이러한 방법은 사실 이런 DXF 등을 작업할때만 사용하는 기술은 아니고, 여러 개발사에서 레이어를 도면관리할때 사용하는 기술에서도 이방법을 많이 사용합니다.

<Border x:Name="RenderHost" ClipToBounds="True">
    <Grid>
        <!-- 메인 맵 비트맵 (전체 엔티티 렌더링) -->
        <Image x:Name="RenderImage"
               RenderOptions.BitmapScalingMode="HighQuality"
               Stretch="None"/>
        
        <!-- 호버 하이라이트 전용 오버레이 (해당 엔티티만 렌더링) -->
        <Image x:Name="HoverOverlayImage"
               RenderOptions.BitmapScalingMode="HighQuality"
               Stretch="None" IsHitTestVisible="False"/>
    </Grid>
</Border>

 

그리고, 히트테스트시에 백그라운드 스레드로 돌리는데, 이때도 중요한 관점은 간격없이 그냥 쓰레드를 돌렸다가는 CPU과부하 걸리므로, 적당한 ms 간격으로 제한해야 합니다. 

private void UpdateHoverAsync(double wx, double wy)
{
    var now = DateTime.UtcNow;
    if ((now - _lastHoverCheck).TotalMilliseconds < 50) return;
    _lastHoverCheck = now;
    // ...
}

 

히트테스트는 UI스레드가 블로킹하지 않도록 Task.Run으로 반드시 액션들을 보장해주어야 한다. 새로운 히트 테스트가 시작하면 CancellationToken으로 즉시 취소해야합니다. 액션들이 계속 쌓여나가면 UI는 적당히 마우스를 흔들었을때 뻗어버리게 되겠죠.

_hoverCts.Cancel();
_hoverCts = new CancellationTokenSource();
var ct = _hoverCts.Token;

_ = Task.Run(() =>
{
    double bestDist = double.MaxValue;
    int    bestIdx  = -1;
    string? bestLayer = null;

    foreach (var (key, doc, hidden) in docsSnapshot)
    {
        if (ct.IsCancellationRequested) return;

        for (int i = 0; i < doc.Entities.Count; i++)
        {
            var entity = doc.Entities[i];
            // 숨겨진 서브레이어는 건너뜀
            if (hidden?.Contains(entity.LayerName ?? "") == true) continue;

            // 1차 필터: BoundingBox로 빠르게 제외
            // ...
            
            // 2차: 정밀 거리 계산
            // ...
        }
    }

    Dispatcher.InvokeAsync(() =>
    {
        _hoverEntityIndex = bestIdx;
        _hoverLayerName   = bestLayer;
        HoverFeatureChanged?.Invoke(bestLayer, bestIdx);
        RenderHoverOverlay();
    });
}, ct);

호버된 엔티티를 시각적으로 골드글로우(말이 있어보여서 그렇치 그냥 금색 처리) 처리하는데, 좀더 이쁘게 하기 위해 2번 겹쳐서 그려서 약간은 금색이 번지는 효과를 입혀줍니다. 

public void RenderHover(.........)
{
    // 외곽 글로우 (주 라인이 되는 것보다 넓고 반투명의 노란색)
    var glowPen = new Pen(
        new SolidColorBrush(Color.FromArgb(80, 255, 215, 0)), 7.0);
    
    // 내부 선 (선명한 라인의 색)
    var innerPen = new Pen(
        new SolidColorBrush(Color.FromArgb(200, 255, 230, 100)), 2.5);

    for (int i = 0; i < doc.Entities.Count; i++)
    {
    	// ...
    }
}

 UI 호버 효과 — DataTrigger 활용

속성테이블이나 속성창에 어떤 수치지형도의 아이템인지 약 110개의 아이템 중에서 맵에서 마우스 오버가 되어 골드글로우가 된 Object를 감별해 내기 위해서 그리드에서 Trigger를 이용해서 Activated 해줍니다. 

 

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band