위성 정보를 3D 지구(Digital Globe)에 시각화

가장 추천하는 방식 3가지를 정리해 드립니다.

카테고리: uploads | 읽기시간: 10분 | 원문: 다운로드

위성 정보를 3D 지구(Digital Globe)에 시각화

가장 추천하는 방식 3가지를 정리해 드립니다.


1. CesiumJS (가장 추천하는 표준 방식)

웹 기반 3D 지구 시각화에서 업계 표준에 가깝습니다. 위성 궤도 시각화, 지형 렌더링, 시계열 데이터 처리에 특화되어 있습니다.


2. NASA WebWorldWind

NASA에서 관리하는 오픈소스 가상 지구 SDK입니다. CesiumJS와 유사하지만, 좀 더 학술적이고 공공 데이터 연동에 친화적입니다.


3. Deck.gl + Mapbox/MapLibre

데이터 분석가나 데이터 사이언티스트들이 선호하는 방식입니다. Uber에서 만든 대용량 데이터 시각화 라이브러리인 Deck.gl을 사용합니다.


💡 어떤 것을 선택해야 할까요?

목적 추천 도구
위성 궤도 및 이동 경로 시각화 CesiumJS
대규모 데이터 분석 및 히트맵 Deck.gl
모바일 앱(네이티브) 개발 WebWorldWind

구현 팁: 데이터 형식

위성 위치 정보를 다루신다면 보통 TLE(Two-Line Element) 형식을 사용하게 됩니다. 이를 위도/경도/고도로 변환하기 위해 satellite.js 같은 라이브러리를 함께 사용하시는 것을 권장합니다.

위성 정보를 3D 지구(Digital Globe)에 시각화하는 것은 데이터의 직관적인 이해를 돕는 아주 멋진 프로젝트가 될 것 같네요! 오픈소스 생태계에는 이 분야를 꽉 잡고 있는 강력한 도구들이 있습니다.

가장 추천하는 방식 3가지를 정리해 드립니다.


1. CesiumJS (가장 추천하는 표준 방식)

웹 기반 3D 지구 시각화에서 업계 표준에 가깝습니다. 위성 궤도 시각화, 지형 렌더링, 시계열 데이터 처리에 특화되어 있습니다.


2. NASA WebWorldWind

NASA에서 관리하는 오픈소스 가상 지구 SDK입니다. CesiumJS와 유사하지만, 좀 더 학술적이고 공공 데이터 연동에 친화적입니다.


3. Deck.gl + Mapbox/MapLibre

데이터 분석가나 데이터 사이언티스트들이 선호하는 방식입니다. Uber에서 만든 대용량 데이터 시각화 라이브러리인 Deck.gl을 사용합니다.


💡 어떤 것을 선택해야 할까요?

목적 추천 도구
위성 궤도 및 이동 경로 시각화 CesiumJS
대규모 데이터 분석 및 히트맵 Deck.gl
모바일 앱(네이티브) 개발 WebWorldWind

구현 팁: 데이터 형식

위성 위치 정보를 다루신다면 보통 TLE(Two-Line Element) 형식을 사용하게 됩니다. 이를 위도/경도/고도로 변환하기 위해 satellite.js 같은 라이브러리를 함께 사용하시는 것을 권장합니다.

--

추천해 드린 세 가지 오픈소스 라이브러리의 실제 구현 예시 이미지들을 찾아보았습니다. 각 도구가 위성 데이터를 어떤 느낌으로 표현하는지 확인해 보세요.

1. CesiumJS: 정밀한 위성 궤도 시각화

가장 전문적인 느낌을 주며, 실제 위성의 고도와 궤적(Orbit)을 3D 공간상에 매우 정확하게 투영합니다. 시간 흐름에 따른 위성의 움직임을 부드럽게 보여주는 것이 강점입니다.

https://cesium.com/industries/aerospace/

2. NASA WebWorldWind: 학술적 데이터 시각화

NASA에서 제공하는 만큼 지형 데이터나 대기 데이터(Heatmap)를 지구본 위에 겹쳐서(Overlay) 보여주는 데 강점이 있습니다. 분석용 대시보드 형태의 프로젝트에 자주 활용됩니다.

https://worldwind.arc.nasa.gov/spacebirds/index.html

3. Deck.gl: 대용량 데이터 및 연결 시각화

수많은 위성 노드(Node)나 지상국과의 연결선(Arc)을 화려하고 현대적인 그래픽으로 표현하고 싶을 때 적합합니다. 웹 브라우저에서도 매우 빠르고 세련된 효과를 낼 수 있습니다.

https://codepen.io/AdriSolid/pen/RwoQeeR

--

세 가지 모두 오픈소스로 공개되어 있으며, 상업적 이용이 가능한 라이선스를 채택하고 있습니다. 각 프레임워크의 오픈소스 현황과 특징을 정리해 드릴게요.


1. CesiumJS

2. NASA WebWorldWind

3. Deck.gl


📊 요약 및 추천

프레임워크 라이선스 관리 주체 추천 용도
CesiumJS Apache 2.0 Cesium GS 실시간 위성 궤도, 전문적인 3D GIS 구축
WebWorldWind Apache 2.0 NASA 학술용, 기상/해양 데이터 시각화
Deck.gl MIT vis.gl (OpenJS) 대시보드 내 데이터 인포그래픽, 고성능 렌더링

현재 가장 활발하게 쓰이고 커뮤니티 지원이 좋은 것은 CesiumJSDeck.gl입니다. 특히 위성의 물리적 궤적과 시간 흐름을 다루신다면 CesiumJS가 압도적인 편의성을 제공할 것입니다.

--

각 프레임워크의 특성에 맞춰 위성 위치(위도, 경도, 고도)를 3D 지구에 배치하는 가장 기초적인 스켈레톤(Skeleton) 코드입니다. 모든 예제는 별도의 빌드 도구 없이 HTML 파일 하나로 실행해 볼 수 있도록 CDN 방식을 사용했습니다.


1. CesiumJS: 위성 엔티티 배치 (Entity API)

Cesium은 위성을 'Entity'로 취급하며, 시간축에 따른 움직임을 구현하기 가장 좋습니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <script src="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Cesium.js"></script>
    <link
      href="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Widgets/widgets.css"
      rel="stylesheet"
    />
  </head>
  <body>
    <div id="cesiumContainer" style="width: 100%; height: 100vh;"></div>
    <script>
      // Cesium.Ion.defaultAccessToken = 'YOUR_ACCESS_TOKEN'; // 지형 데이터를 쓰려면 토큰 필요
      const viewer = new Cesium.Viewer("cesiumContainer", {
        terrainProvider: Cesium.createWorldTerrain(),
      });

      // 위성 엔티티 추가
      const satellite = viewer.entities.add({
        name: "K-Sattie-01",
        position: Cesium.Cartesian3.fromDegrees(127.0, 37.5, 500000), // 경도, 위도, 고도(m)
        point: { pixelSize: 10, color: Cesium.Color.RED },
        label: {
          text: "Satellite A",
          font: "14pt monospace",
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          pixelOffset: new Cesium.Cartesian2(0, -9),
        },
      });

      viewer.zoomTo(satellite);
    </script>
  </body>
</html>

2. NASA WebWorldWind: 레이어 기반 배치

WorldWind는 전통적인 GIS 레이어 방식을 따르며, RenderableLayer 위에 객체를 올립니다.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://files.worldwind.arc.nasa.gov/artifactory/web/0.9.0/worldwind.min.js"></script>
  </head>
  <body>
    <canvas id="canvasOne" style="width: 100%; height: 100vh;"></canvas>
    <script>
      const wwd = new WorldWind.WorldWindow("canvasOne");

      // 기본 지도 레이어 추가
      wwd.addLayer(new WorldWind.BMNGOneImageLayer());
      wwd.addLayer(new WorldWind.CoordinatesDisplayLayer(wwd));

      // 위성 표시 레이어 생성
      const satelliteLayer = new WorldWind.RenderableLayer("Satellites");
      wwd.addLayer(satelliteLayer);

      // 위성 지점(Placemark) 설정
      const position = new WorldWind.Position(37.5, 127.0, 500000); // 위도, 경도, 고도
      const placemark = new WorldWind.Placemark(position, false, null);
      placemark.label = "Satellite B";

      const attributes = new WorldWind.PlacemarkAttributes(null);
      attributes.imageSource =
        "https://worldwind.arc.nasa.gov/web/examples/images/white-dot.png";
      placemark.attributes = attributes;

      satelliteLayer.addRenderable(placemark);
    </script>
  </body>
</html>

3. Deck.gl: 대용량 데이터 시각화 (ScenegraphLayer)

Deck.gl은 대량의 점 데이터를 GPU로 렌더링하는 데 최적화되어 있습니다. 아래는 위성을 구체(Point)로 표현하는 기본 구조입니다.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
    <script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
    <link
      href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css"
      rel="stylesheet"
    />
  </head>
  <body style="margin: 0;">
    <div id="container" style="width: 100vw; height: 100vh;"></div>
    <script>
      const { DeckGL, GeoJsonLayer, ScatterplotLayer } = deck;

      new DeckGL({
        container: "container",
        initialViewState: {
          longitude: 127.0,
          latitude: 37.5,
          zoom: 2,
          pitch: 45,
        },
        controller: true,
        layers: [
          new ScatterplotLayer({
            id: "satellite-layer",
            data: [{ position: [127.0, 37.5, 5000], name: "Sat-C" }], // [경도, 위도, 고도]
            getPosition: (d) => d.position,
            getFillColor: [0, 255, 128],
            getRadius: 100000,
            pickable: true,
          }),
        ],
        views: new deck.GlobeView(), // 지구본 형태로 보기
      });
    </script>
  </body>
</html>

💡 팁: 무엇부터 시작할까요?

3가지 프레임워크 모두에서 실제 위성 데이터를 다루는 핵심은 TLE (Two-Line Element) 데이터를 웹 브라우저가 이해할 수 있는 위도, 경도, 고도로 변환하는 것입니다.

이 변환을 위해 전용 자바스크립트 라이브러리인 **satellite.js**를 필수적으로 함께 사용해야 합니다.

각 프레임워크별로 TLE 데이터를 활용하여 실시간으로 위성 위치를 갱신하는 구현 스켈레톤을 소개합니다. 이 코드를 복사하여 .html로 저장하면 즉시 ISS(국제우주정거장)의 실시간 위치를 확인할 수 있습니다.


필수 공통 데이터: ISS의 TLE

모든 예제는 현재 시점의 ISS TLE 데이터를 사용합니다. (실제 서비스에서는 이 데이터를 백엔드 API에서 실시간으로 받아와야 합니다.)

// 국제우주정거장(ISS)의 TLE 데이터 (실시간 갱신 필요)
const tleLine1 =
  "1 25544U 98067A   24089.15582236  .00015692  00000-0  27889-3 0  9999";
const tleLine2 =
  "2 25544  51.6416 195.1432 0004558 102.7711 319.2483 15.49534708445775";

1. CesiumJS + satellite.js (가장 권장됨)

Cesium은 CallbackProperty를 이용해 매 프레임마다 위성의 위치를 계산하여 애니메이션처럼 보여주는 방식에 가장 최적화되어 있습니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <script src="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Cesium.js"></script>
    <link
      href="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Widgets/widgets.css"
      rel="stylesheet"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/satellite.js/4.1.3/satellite.min.js"></script>
  </head>
  <body>
    <div id="cesiumContainer" style="width: 100%; height: 100vh;"></div>
    <script>
      // Cesium.Ion.defaultAccessToken = 'YOUR_ACCESS_TOKEN';
      const viewer = new Cesium.Viewer("cesiumContainer");

      // ISS TLE
      const tleLine1 =
        "1 25544U 98067A   24089.15582236  .00015692  00000-0  27889-3 0  9999";
      const tleLine2 =
        "2 25544  51.6416 195.1432 0004558 102.7711 319.2483 15.49534708445775";
      const satrec = satellite.twoline2satrec(tleLine1, tleLine2);

      // [핵심] 실시간 위치 계산 함수
      function getSatellitePosition(time) {
        const date = Cesium.JulianDate.toDate(time);
        const positionAndVelocity = satellite.propagate(satrec, date);
        const gmst = satellite.gstime(date);
        const positionGd = satellite.eciToGeodetic(
          positionAndVelocity.position,
          gmst,
        );

        // Cesium 전용 좌표계(Cartesian3)로 변환
        return Cesium.Cartesian3.fromRadians(
          positionGd.longitude,
          positionGd.latitude,
          positionGd.height * 1000, // satellite.js는 km, Cesium은 m 단위 사용
        );
      }

      // 위성 엔티티 추가 (위치를 CallbackProperty로 지정)
      const issEntity = viewer.entities.add({
        id: "ISS",
        name: "ISS (Real-time)",
        position: new Cesium.CallbackProperty(getSatellitePosition, false), // 매 프레임마다 계산
        point: { pixelSize: 15, color: Cesium.Color.RED },
        label: {
          text: "ISS",
          font: "12pt sans-serif",
          pixelOffset: new Cesium.Cartesian2(0, -15),
        },
        // 궤적 표현 추가
        path: {
          leadTime: 3600,
          trailTime: 3600,
          width: 2,
          material: Cesium.Color.YELLOW,
        },
      });

      // 위성으로 카메라 이동 및 추적
      viewer.trackedEntity = issEntity;
    </script>
  </body>
</html>

2. NASA WebWorldWind + satellite.js

WorldWind는 setInterval을 이용해 주기적으로 placemark의 위치 속성을 직접 갱신하는 방식을 사용합니다.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://files.worldwind.arc.nasa.gov/artifactory/web/0.9.0/worldwind.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/satellite.js/4.1.3/satellite.min.js"></script>
  </head>
  <body>
    <canvas id="canvasOne" style="width: 100%; height: 100vh;"></canvas>
    <script>
      const wwd = new WorldWind.WorldWindow("canvasOne");
      wwd.addLayer(new WorldWind.BMNGOneImageLayer());

      const satelliteLayer = new WorldWind.RenderableLayer("ISS Layer");
      wwd.addLayer(satelliteLayer);

      // ISS TLE & 초기화
      const tleLine1 =
        "1 25544U 98067A   24089.15582236  .00015692  00000-0  27889-3 0  9999";
      const tleLine2 =
        "2 25544  51.6416 195.1432 0004558 102.7711 319.2483 15.49534708445775";
      const satrec = satellite.twoline2satrec(tleLine1, tleLine2);

      // Placemark 생성 (초기 위치 임시 설정)
      const placemark = new WorldWind.Placemark(
        new WorldWind.Position(0, 0, 0),
      );
      placemark.label = "ISS";
      const attributes = new WorldWind.PlacemarkAttributes(null);
      attributes.imageSource =
        "https://worldwind.arc.nasa.gov/web/examples/images/white-dot.png";
      attributes.imageScale = 1.5;
      placemark.attributes = attributes;
      satelliteLayer.addRenderable(placemark);

      // [핵심] 1초마다 위치를 계산하여 업데이트
      setInterval(function () {
        const now = new Date();
        const positionAndVelocity = satellite.propagate(satrec, now);
        const gmst = satellite.gstime(now);
        const positionGd = satellite.eciToGeodetic(
          positionAndVelocity.position,
          gmst,
        );

        // WorldWind 좌표계(Position) 갱신
        placemark.position = new WorldWind.Position(
          satellite.degreesLat(positionGd.latitude), // 라디안 -> 도(degree) 변환
          satellite.degreesLong(positionGd.longitude),
          positionGd.height * 1000, // m 단위
        );

        wwd.redraw(); // 화면 재생성
      }, 1000);
    </script>
  </body>
</html>

3. Deck.gl + satellite.js

Deck.gl은 상태(State) 기반으로 작동하므로, 자바스크립트의 requestAnimationFrame을 이용해 주기적으로 데이터 배열 자체를 새롭게 계산하여 레이어에 넘겨주는 방식을 사용합니다.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
    <script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
    <link
      href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css"
      rel="stylesheet"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/satellite.js/4.1.3/satellite.min.js"></script>
  </head>
  <body style="margin: 0;">
    <div id="container" style="width: 100vw; height: 100vh;"></div>
    <script>
      const { DeckGL, ScatterplotLayer, GlobeView } = deck;

      // ISS TLE
      const tleLine1 =
        "1 25544U 98067A   24089.15582236  .00015692  00000-0  27889-3 0  9999";
      const tleLine2 =
        "2 25544  51.6416 195.1432 0004558 102.7711 319.2483 15.49534708445775";
      const satrec = satellite.twoline2satrec(tleLine1, tleLine2);

      let deckInstance;

      // [핵심] 현재 위치 데이터를 가진 레이어 스냅샷 생성
      function renderLayer() {
        const now = new Date();
        const positionAndVelocity = satellite.propagate(satrec, now);
        const gmst = satellite.gstime(now);
        const positionGd = satellite.eciToGeodetic(
          positionAndVelocity.position,
          gmst,
        );

        const issData = [
          {
            // Deck.gl GlobeView는 [경도, 위도, 고도] 순서 사용
            position: [
              satellite.degreesLong(positionGd.longitude),
              satellite.degreesLat(positionGd.latitude),
              positionGd.height * 1000,
            ],
            name: "ISS",
          },
        ];

        const layer = new ScatterplotLayer({
          id: "iss-layer",
          data: issData,
          getPosition: (d) => d.position,
          getFillColor: [255, 50, 50],
          getRadius: 200000, // m 단위 (크게 설정)
          stroked: true,
          getLineColor: [255, 255, 255],
          lineWidthMinPixels: 2,
        });

        // 기존 DeckGL 인스턴스가 있다면 레이어만 갱신
        if (deckInstance) {
          deckInstance.setProps({ layers: [layer] });
        }
        return layer;
      }

      // 초기 인스턴스 생성
      deckInstance = new DeckGL({
        container: "container",
        views: new GlobeView(), // 지구본 모드
        initialViewState: { longitude: 0, latitude: 0, zoom: 1 },
        controller: true,
        layers: [renderLayer()],
      });

      // 주기적 업데이트 애니메이션 루프
      function animationLoop() {
        renderLayer();
        requestAnimationFrame(animationLoop); // 매 프레임 업데이트 (성능 주의)
      }
      animationLoop();
    </script>
  </body>
</html>

요약

데이터 연동 관점에서 CesiumJSCallbackProperty라는 빌트인 메커니즘을 제공하여 코드가 가장 간결하고 성능이 좋습니다. satellite.js 라이브러리는 공통으로 사용되며 km 단위 좌표를 m 단위로 변환하는 것을 잊지 말아야 합니다.

×preview