IT의 IT 블로그

LOD1 건물 시각화: 공공데이터 기반 3D 빌딩 구축 튜토리얼 본문

프로젝트 회고록

LOD1 건물 시각화: 공공데이터 기반 3D 빌딩 구축 튜토리얼

IT의 IT 블로그 2025. 12. 16. 14:29

LOD 건물 설명

LOD 건물은 **Level of Detail(세밀도)**의 약자로,
카메라 거리나 시점에 따라 3D 모델의 디테일 수준을 단계적으로 조절하여
데이터 부하를 줄이고 렌더링 성능을 최적화하는 기법을 적용한 건물을 의미합니다.

LOD는 일반적으로 LOD1, LOD2, LOD3, LOD4 단계로 구분되며,
단계가 높아질수록 건물의 형태, 지붕 구조, 창문, 내부 공간 등
표현되는 정보의 상세도가 증가합니다.

이번 정리에서는 LOD1 수준의 건물 데이터를 활용하여 Cesium에 건물을 표시하는 과정을 중심으로 설명합니다.

1. 브이월드(VWorld) 건물 데이터 활용

브이월드 공간정보 오픈플랫폼에서는
국가가 보유한 공개 가능한 공간정보를 누구나 자유롭게 활용할 수 있도록 제공하고 있습니다.

아래 경로를 통해 전국 용도별 건물정보
CSV 및 SHP(Shapefile) 형태로 다운로드할 수 있습니다.

용도별 건물정보

용도별 건물정보

 

브이월드

공간정보 오픈플랫폼은 국가가 보유하고 있는 공개 가능한 공간정보를 모든 국민이 자유롭게 활용할 수 있도록 다양한 방법을 제공합니다.

www.vworld.kr

 

2. CSV / SHP 데이터 구조 확인

다운로드한 CSV 파일을 열어보면
건축물대장 기반의 건물 속성 정보가 포함되어 있음을 확인할 수 있습니다.
(건물 용도, 층수, 주소 정보 등)

SHP 파일의 경우, 여러 개의 파일로 구성되어 있으며
이를 QGIS에서 불러오면 해당 지역의 건물 형상(geometry) 이 지도 위에 표시됩니다.

예를 들어 용산구에 해당하는 SHP 파일을 QGIS에 로드하면,
용산구 전역의 건물 폴리곤 데이터가 화면에 시각화됩니다.

 

예시

 

SHP 파일은 열어보면 아래와 같은 shp 파일이 존재합니다.

 

QGIS 에 SHP 파일을 올려보면 해당하는 지역에 대한 geom 데이터가 그려지면서 화면에 표출되게 됩니다

용산구에 해당하는 SHP 파일

 

올려진 데이터의 속성 테이블을 열어서 데이터를 확인해보면 방금 위에서 확인한 CSV 파일의 속성 정보가 들어가 있음을 확인 할 수 있습니다. 단 컬럼 명이 a1~a35 형태로 들어가있으며 각각 정보는 CSV 파일의 컬럼과 동일합니다.

 

 

 

특정 지역만 추출하는 방법

용산구 전체가 아닌 특정 지역만 필요할 경우,
다음과 같은 방식으로 데이터를 필터링할 수 있습니다.

  1. 속성 테이블 상단의 ‘표현식을 이용해 객체 선택’ 기능을 사용하여 조건 필터링
  2. 지도 화면에서 마우스로 특정 영역을 직접 선택

선택이 완료되면
레이어 우클릭 → 내보내기 → 선택한 객체를 다른 이름으로 저장
을 통해 선택한 영역만 별도의 SHP 파일로 추출할 수 있습니다.

 

 

1. 속성 테이블 상단의 ‘표현식을 이용해 객체 선택’ 기능을 사용하여 조건 필터링

 

 

2. 지도 화면에서 마우스로 특정 영역을 직접 선택

레이어 오른쪽 클릭 -> 내보내기 -> 선택한 객체를 다른 이름으로 저장. 클릭 하면 해당 하는 지역만 추출이 가능합니다.

 

 

4. DB 저장 및 GeoServer 연계

추출한 SHP 파일은 DB 관리자(PostGIS 등) 를 이용해
원하는 데이터베이스에 저장합니다.

DB에는 건물의 geom(geometry) 데이터가 포함되며,
이 geom 데이터를 기반으로 GeoServer에서 레이어를 구성합니다.

GeoServer에 레이어가 등록되면, 
WMS(Web Map Service)를 통해 클라이언트에서 건물 데이터를 불러올 수 있습니다.

 

 

 

DB에는 geom 데이터 형식이 들어가있습니다. 

해당 geom 데이터를 활용하여 Geoserver 에서 불러와서 레이어를 구성하였습니다.

5. Cesium에서 LOD1 건물 표시

이제 JS 단에서는
GeoServer의 WMS URL을 호출하여 데이터를 가져온 뒤,
Cesium의 Entity 속성을 활용해 건물을 화면에 표시하면 됩니다.

LoD1 건물 로딩 및 렌더링 코드 예시

// LoD1 건물 레이어 목록
const BUILDING_LAYERS = ['base:tbl_bld_Itaewon'];


// LoD1 건물 데이터 요청
function setLod1CommandQueue(proxyUrl, geoserverUrl, [minX, minY, maxX, maxY]) {

    // 레이어당 최대 건물 개수
    const perLayerLimit = Math.ceil(
        MAX_BUILDINGS / (BUILDING_LAYERS.length || 1)
    );

    // 레이어별 WFS 요청 생성
    const requests = BUILDING_LAYERS.map(layer => {
        const wfsUrl =
            `${geoserverUrl}base/ows` +
            `?service=WFS&version=1.1.0&request=GetFeature` +
            `&typeName=${layer}` +
            `&srsName=EPSG:4326` +
            `&bbox=${minX},${minY},${maxX},${maxY},EPSG:4326` +
            `&outputFormat=application/json`;

        return Cesium.GeoJsonDataSource.load(
            proxyUrl + encodeURIComponent(wfsUrl)
        );
    });

    // 모든 레이어 로딩 완료 후
    Promise.all(requests)
        .then(results => {
            const entities = [];

            // 레이어별 건물 엔티티 취합
            results.forEach(result => {
                result.entities.values
                    .slice(0, perLayerLimit)
                    .forEach(e => {
                        if (entities.length < MAX_BUILDINGS) {
                            entities.push(e);
                        }
                    });
            });

            // 건물 화면에 표시
            drawBuildingLoD1(entities);
        })
        .catch(err => {
            console.error('LoD1 건물 로딩 오류:', err);
        });
}


// LoD1 건물 렌더링
function drawBuildingLoD1(entities) {
    entities.forEach(entity => {
        setupBuildingEntity(entity);      // 건물 스타일/속성 설정
        viewer.entities.add(entity);      // Cesium Viewer에 추가
    });
}


// 개별 건물 엔티티 설정
function setupBuildingEntity(entity) {

    const polygon = entity.polygon;
    if (!polygon) return; // 폴리곤 없는 데이터는 제외

    // 건물 고유키
    const bldKey = entity?.properties?._BLDRGST_PK?.value;

    // 층수 (기본 1층)
    const floorCount = entity?.properties?._GRND_FLR?.value ?? 1;

    // 건물 높이 계산
    const height = floorCount * HEIGHT_PER_FLOOR;

    // 건물 외형 설정
    polygon.material = getBuildingColor(bldKey);                 // 건물 색상
    polygon.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND;
    polygon.extrudedHeightReference = Cesium.HeightReference.RELATIVE_TO_GROUND;
    polygon.extrudedHeight = height;                              // 높이
}

6. 결과 확인

Cesium 화면에서 결과를 확인해보면,
선택한 지역의 건물들이 LOD1 형태로 건물이 표시되는 것을 확인할 수 있습니다.

 

마무리

지금까지
브이월드 건물 데이터 → QGIS 필터링 → DB 저장 → GeoServer 연계 → Cesium LOD1 렌더링
과정을 통해 LOD1 건물을 화면에 표시하는 방법을 정리해보았습니다.

이 구조를 기반으로
LOD2·LOD3 확장, 성능 최적화, 속성 연동 등도 단계적으로 적용할 수 있습니다.