해안 높이 보정 제거 (2026-10-30)

하조대·기사문 높이 격자는 Copernicus 좌표 변환·보간값만 사용한다. OSM 해안선을 이용한 해안 높이 낮추기·최소 높이·바다 칸 0m 덮어쓰기를 제거했다. OSM 해안선·표면 마스크는 화면 표시와 이동 경계에 유지하며 관악산 지형은 변경하지 않았다. 아래 해안 보정 설명은 제거 전 개발 기록이다.

해보러가기

매일 일출 시각에 화면을 촬영하고 Meta Threads에 게시하는 Linux 작업은 자동 게시 설정 가이드를 참고하세요.

현재 과천 건물: VWorld 단독 (2026-10-30)

OSM 건물의 외곽선·높이·층수와 OSM 대조 기반 선별을 제거했다. VWorld 원본에서 독립적으로 재생성한 6,743개를 표시한다(높이 자료 1,756개 / 층수 환산 953개 / 임시 높이 4,034개). VWorld 내부 겹침 183개를 제외했다. 아래 OSM·VWorld 통합 설명은 이전 작업 기록이며 현재 구성에 적용되지 않는다. 해안선·정상 구조물 위치의 OSM 사용은 유지한다.

재생성은 scripts/prepare-vworld-buildings.py → npm run prepare:buildings → npm run build. 현재 출처·CC BY 2.0 KR 이용조건은 /buildings/source.html, 세부 기록은 data/references/gwacheon/README.md를 따른다.

공동 관측 서버

관악산 채널 1은 최대 100명을 수용합니다. Node.js + Socket.IO로 이동·감정표현·우천 장비를 공유하고 MongoDB에는 접속 기록만 저장합니다. npm run build 후 npm start로 실행합니다. 기본 주소는 http://127.0.0.1:2027입니다.

환경설정·개발 실행·검증 방법과 운영 한계는 서버 설명을 참고하세요. 기본 MongoDB는 172.16.0.7:27017의 sunrise.visitSessions이며 .config.example.json을 .config.json으로 복사한 뒤 변경합니다.

모바일·웹 몰입형 UI (2026-10-30)

  • 장면에는 장소·시간·메뉴와 인사·소원빌기·장비 버튼만 표시합니다. 날짜·시간, 관측 정보, 지도·시점, 접속, 설정, 출처는 버튼으로 여는 팝업에 모았습니다.
  • 팝업은 닫기·바깥 영역 클릭·Esc로 닫습니다. 열린 동안 배경 이동과 캐릭터 단축키를 차단하고, 닫으면 열었던 버튼으로 포커스가 돌아옵니다.
  • 미니맵은 기본 숨김입니다. 메뉴 → 지도·시점 → 미니맵을 화면에 계속 표시로 켜고 지도 위 닫기 버튼으로 숨깁니다.
  • 함께 보기에서는 날짜·시간을 공유합니다. 시간 팝업의 함께 보기·접속에서 혼자 보기로 전환하면 직접 조절할 수 있습니다.
  • 소원빌기는 기존과 같이 일출 전후 5분에 활성화됩니다. 메뉴의 조작 방법에서 사용 조건과 단축키를 확인할 수 있습니다.
  • 메뉴 → 출처·오픈소스에서 데이터·모델 출처와 /licenses.txt의 설치 버전별 라이브러리 저작권·라이선스 원문을 확인할 수 있습니다.

앱 진입 흐름 (2026-10-30)

시작 화면에는 직접 그린 일출 아이콘과 보러가기, 혼자보기 | 같이 보기만 표시합니다. 달·별이 보이는 밤을 0.3초 보여준 뒤, 4.5초 동안 해가 능선 위로 충분히 떠오르며 별과 달이 희미해지고 마지막 장면을 유지합니다. 숨겨진 탭에서는 재생을 멈추고, 동작 줄이기 설정에서는 정지 장면을 표시합니다.

배경은 기존 관악산 DSM·정상 지형 보정과 createSky/createSun/createNightSky를 그대로 렌더링합니다. 기존 정상 관측점에서 방위각 110° 방향을 바라보며, 달이 같은 화면에 들어오는 2026-11-06 05:40~07:45 KST의 천체 계산을 4.5초로 압축한 연출입니다. 별은 분위기용 고정 배치이며 현재 날씨의 실시간 표시가 아닙니다. 별도의 능선 그림이나 CSS 태양은 사용하지 않습니다. 입장 시 배경 렌더러의 GPU 자원을 해제합니다.

  • 혼자보기: 서버 목록 조회 없이 관악산 장면으로 바로 입장합니다.

  • 같이 보기: 채널 선택 → 지형·캐릭터 로딩 및 입장 승인 → 관악산 화면.

  • 현재 관악산 공동 방을 채널 1로 사용한다.

  • /api/lobby에서 서버 목록, 채널 정원·인원·입장 가능 여부를 조회한다. 선택 화면에서는 방문 기록이나 3D 장면을 생성하지 않는다.

  • 개발 시 프런트엔드(npm run dev)와 백엔드(npm run dev:server)를 함께 실행한다. Vite가 /api와 /socket.io를 백엔드 2027번 포트로 전달한다.

  • 서버 조회 실패·입장 실패는 재시도할 수 있다. 명시적인 ?mode=solo 비교 화면과 개발용 ?profile 경로는 기존 직접 진입을 유지한다.

아래 내용은 각 기능의 개발 기록이다.

Open-Meteo 기상 연동 (2026-10-30)

  • 선택 날짜·시간에 맞춰 전체 운량(%), 시정(m), 강우량(mm/h), 강설량(cm/h)을 조회한다. 운량·시정은 Unix 시각 기준으로 보간하고 구름·안개·태양 감쇠를 부드럽게 전환한다. 비는 rain + showers(재분석은 rain), 눈은 snowfall을 사용한다. 누적량은 각 타임스탬프 직전 한 시간 구간에 적용하며 시간 사이를 보간해 가짜 진눈깨비를 만들지 않는다. 태양 위치와 천문 일출 계산은 바꾸지 않는다.
  • 최근 5일최대 16일 예보 범위는 Forecast API, 그보다 과거인 2022년 이후는 Historical Forecast API를 사용한다. 19402021년은 Historical Weather API의 운량·비·눈을 반영하며 시정 미제공을 표시한다. 모델/변수별 결측이 있을 수 있다. 강우·강설 결측은 미제공으로 표시하며 해당 입자를 끈다. 범위 밖·연결 실패는 표시와 함께 기본 하늘로 복귀한다.
  • 강수량에 따라 카메라 주변의 빗줄기·눈송이 수가 달라지고 빗줄기 길이·불투명도도 변한다. 눈은 천천히 흔들리며 떨어지고, 두 자료가 모두 양수이면 함께 내린다. 렌더링 강도만 부드럽게 전환하며 정지된 CUSTOM 시각에도 입자는 움직인다. 지형/해수면 높이와 깊이 검사를 적용한다. 적설·젖은 표면·지붕 충돌·실측 바람은 구현하지 않는다. 맑으면 강수 애니메이션으로 인한 추가 렌더링을 중단하며 입자 수에는 성능 상한이 있다.
  • 요청은 관측 장소의 고정 좌표와 UTC 날짜별로 캐시한다(최대 8개, 30분 갱신). 슬라이더를 움직일 때 매번 요청하지 않고, 이전 날짜의 늦은 응답은 현재 장면에 적용하지 않는다. 연결 실패는 2분 후 재시도하며 CUSTOM 상태에서도 작동한다. 외부 통신은 15초 후 중단한다.
  • 수치예보/재분석에 따른 시각적 근사다. 실제 동쪽 수평선의 구름 모양·구름 차폐나 산 정상의 국지 시정을 재구성하지 않는다. 하늘과 태양에 같은 방향별 절차적 구름 분포를 적용하며, 운량 100%에서는 수평선까지 불투명한 구름층이 덮고 태양 원반·halo를 가린다. 구름의 음영은 태양 고도에 맞춰 낮·새벽·밤으로 변한다. 선형 안개와 운량·시정 기반 직사광 감쇠를 사용하며, 자료가 없는 시정은 짙은 안개로 해석하지 않는다.
  • 추가 의존성·API 키·백엔드는 없다. 무료 API는 비상업용이며 배포 용도에 맞는 이용 조건을 확인해야 한다. 화면에 출처를 표시한다: Open-Meteo 문서, 과거 예보, 재분석, 이용 조건.
  • 구현: src/weather/weather.ts, src/scene/precipitation.ts, 조정 상수: src/constants/weather.ts, src/constants/precipitation.ts, 검증: tests/weather.test.ts, tests/precipitation.test.ts.

과천 건물 공식 자료 보완 (2026-10-30)

사용자가 제공한 VWorld AL_D010_41_20260719.zip을 기존 OSM과 공간 대조해 건물을 1,640개 → 6,692개로 보완했다. 기존 외곽선을 유지하고 겹치지 않는 공식 외곽선 5,052개를 추가했다. 기존 건물의 빈 높이 393건·층수 846건을 보완했으며 두 수치는 중복될 수 있다. 최종 높이 자료 1,771개 / 층수 환산 894개 / 임시 높이 4,027개. 충돌하거나 애매하게 겹치는 자료는 제외 내역으로 남긴다.

재생성은 python scripts/prepare-vworld-buildings.py <원본 ZIP 경로>. 상세 출처·조건은 data/references/gwacheon/README.md와 /buildings/source.html. 현재 지형은 여전히 30m급 DSM이므로 건물 기초의 실제 표고와 매몰 문제는 미해결이다. 아래 2026-10-29 건물 설명은 보완 전 기록이다.

기사문 해변 추가 (2026-10-29)

상단의 기사문 해변 링크 또는 /?site=gisamun으로 이동한다. 관측점은 위도 38.00555°, 경도 128.73195°의 해변 안쪽 임시 위치다. 기존 하조대 지형을 없애지 않고 기사문 중심 3×3km 데이터를 추가했다.

  • 기존 N37/N38 DSM을 병합·추출한 높이 파일 362KB, 해안선·마스크·메타데이터 포함 약 416KB.
  • 바다·해안 렌더러, 이동 제한·시간·눈높이·조감 기능 재사용. 기사문항 방향 버튼 제공(항만 구조물 상세 모델은 없음).
  • 기사문 모래 영역은 OSM에 없어 해당 해안선을 서쪽으로 35m 확장해 시각적으로 근사했다. 위치·높이·모래 경계는 현장 측량값이 아니다.
  • 재생성: python scripts/prepare-hajodae.py gisamun. 자료 목록은 data/references/gisamun/README.md, 공개 설명은 /terrain/gisamun/source.html.
  • 2026-10-29 06:52/06:55/07:00의 태양 방향이 구현된 지형에 가리지 않는 것을 자동 검사했고, 06:55에는 브라우저에서 원반을 확인했다. 모든 날짜·실제 현장 조건에 대한 보장은 아니다.

하조대 해안 (2026-10-29)

기본 화면은 하조대 해변이다. /?site=hajodae와 /?site=gwanaksan 또는 좌측 상단 장소 링크로 전환한다. 아래의 관악산 설명은 기존 장면에 해당한다.

  • 하조대 임시 관측점: 위도 38.02310°, 경도 128.72540°. 해변 내부 OSM 영역을 기준으로 선정했으며 현장 측량 좌표가 아니다.
  • Copernicus 원본 약 64MB 중 북쪽 타일에서 3×3km만 추출했다. 30m급 원본을 10m 간격 301×301로 보간한 높이 격자 362,404 bytes, 해안선 JSON 19,339 bytes, PNG 마스크 18,208 bytes다. 메타데이터 포함 약 403KB만 하조대 장면이 요청한다(공통 JS 등 제외).
  • 실제 OSM 해안선과 모래사장 영역으로 육지/바다를 구분한다. 바다는 지구 곡률, 시각적 물결·거품·햇빛 반사를 표현한다. 날짜·시간·눈높이, WASD, 시작점 복귀를 지원하며 해안선 밖 바다로 이동하지 않는다. 해안 조감은 관측용이 아닌 배치 확인용 카메라다.
  • 해안선 30m 이내 지면은 해수면에 접하도록 시각적으로 보정했다. 원본 DSM이 0m인 초기 해변 지점은 0.2m로 보정해 표시한다. 실제 지반 표고·조석·수심·파고를 의미하지 않는다. 작은 섬과 절벽의 세밀한 높이, 정자·등대·건물 모델은 미구현이다.
  • 해안선 마스크는 2m 간격, 지형 격자는 10m 간격이지만 원본 측량 정확도가 높아진 것은 아니다. 3×3km 밖은 육지 없이 바다를 연장한다. 실제 절벽이나 모래턱의 세밀한 높이는 재현하지 못한다.
  • 전처리: 기존 .tools/terrain의 rasterio/numpy/pyproj 환경에서 python scripts/prepare-hajodae.py. 상수는 src/constants/coast.ts, 출처·보정은 public/terrain/hajodae/metadata.json과 /terrain/hajodae/source.html에 기록했다. 원본·자료 목록은 data/references/hajodae/README.md 참조.
  • 검증: 기존 기능 포함 33개 테스트와 빌드 통과. 브라우저에서 낮/일출 전후, 해안 조감, 관악산 전환을 확인했다. 동쪽으로 약 52m 이동 시 해안선에서 멈추는 것도 확인했다. 내장 브라우저에서는 Pointer Lock 대신 기존 드래그/키보드 대체 조작을 사용한다.

기존 관악산 장면

고정 관측점에서 날짜·시각에 따른 태양의 실제 방위각과 겉보기 고도각을 관찰하는 PC 웹앱입니다. TypeScript + Vite + Three.js + SunCalc 2.0.1을 사용하며 백엔드는 없습니다.

Copernicus 30m급 DSM 두 타일에서 관측점 중심 24×24km를 추출한 실제 높이 격자를 적용했습니다. 브라우저는 원본 79.3MB 대신 약 2.57MB의 정적 격자를 불러옵니다. 건물 개별 모델과 항공사진 텍스처는 포함하지 않습니다.

설치 및 실행

Node.js 22.18 이상(검증 환경: 24.19), npm이 필요합니다.

cd D:\project\sunrise
npm ci
npm run dev

터미널에 표시된 Local 주소를 일반 Chrome 또는 Edge 탭에서 엽니다. 기본 주소는 http://127.0.0.1:5173/이며, 포트가 사용 중이면 Vite가 다음 포트를 사용합니다. 내장 웹뷰에서는 Pointer Lock이 지원되지 않을 수 있습니다.

npm test                # 좌표, 날짜, 시간 전환, 독립 천문 계산 대조
npm run verify:solar    # NOAA 대조 표 출력
npm run build           # TypeScript 검사 + dist/ 생성
npm run preview         # production build 로컬 확인

사용법

  • 기본은 LIVE입니다. 매초 시스템의 현재 Date를 새로 읽습니다. 탭 복귀/창 포커스 시 즉시 동기화합니다.
  • 하늘을 클릭하면 마우스 시점 조작이 시작됩니다. ESC로 잠금을 풀고 UI를 조작합니다.
  • 초기 시선은 동쪽(+X), 좌우 회전은 360°, 상하 회전은 ±85°입니다. 카메라 위치는 고정이며 WASD·휠 이동은 없습니다.
  • 날짜 또는 시간 슬라이더를 바꾸면 CUSTOM으로 전환되고 시간이 멈춥니다. 슬라이더 범위는 00:00~23:59, 간격은 1분입니다.
  • 실시간 복귀 버튼으로 현재 날짜와 시각으로 돌아옵니다.
  • 눈높이 슬라이더로 160~195cm, 1cm 간격으로 관측 높이를 조절합니다(기본 170cm). 선택값은 신장이 아닌 지면에서 눈까지의 높이로 직접 사용하며, 신장과 눈높이의 차이는 추정하지 않습니다. 카메라·기준선·태양 배치·일출/일몰 고도 보정이 즉시 갱신됩니다. 날짜·LIVE/CUSTOM 모드·시선 방향은 유지됩니다. 새로고침하면 기본값으로 돌아옵니다.
  • 일출 예시: 2026-03-20 06:50 또는 2026-10-29 07:00, 일출 전 예시: 2026-03-20 06:00.

좌표 및 계산 기준

src/config/observer.ts의 관측점은 미확정 임시값입니다.

설정 값
위도 37.4450° N
경도 126.9640° E
설정 파일의 기존 임시 해발고도 629 m (지형 모드에서 사용하지 않음)
지형 모드의 관측점 DSM 표면고도 606.906 m, EGM2008 기준
눈높이 기본 1.7m, UI에서 1.60~1.95m 조절

현장 측량이나 특정 전망대 위치로 검증된 좌표가 아닙니다. 브라우저 GPS는 사용하지 않습니다. 화면에도 임시값이라고 표시합니다.

지형 모드에서는 카메라를 DSM 표면+눈높이(약 608.606m)에 두고, 일출·일몰 계산에도 동일한 높이를 전달합니다. 30m급 DSM 표면은 수목·구조물을 포함할 수 있어 실제 전망대 바닥 높이와 다를 수 있습니다. 관측점 위도·경도를 바꾸면 아래 전처리를 다시 실행해야 합니다(정합 검사로 잘못된 데이터 조합을 거부).

  • SunCalc 2.x는 북쪽 0°·동쪽 90°·남쪽 180°·서쪽 270° 방위각과 대기 굴절이 보정된 고도각을 도 단위로 반환합니다. 1.x의 남쪽 기준/라디안 변환을 적용하지 않습니다.
  • 월드 좌표는 +X 동쪽, +Y 위쪽, -Z 북쪽입니다. 천문 계산은 Three.js와 독립이며 방향 벡터를 렌더러에 전달합니다.
  • 태양 원반 각지름은 0.53°입니다. 별도 halo와 광원 색상 효과를 사용하고 Sky의 기본 원반은 꺼서 중복 표시를 막습니다.
  • 모든 입력·표시는 Asia/Seoul을 사용합니다. 현재는 UTC+09:00이며, 과거 날짜는 IANA의 과거 시차/DST를 따릅니다. 선택 가능 범위는 1901~2099년입니다. 과거 DST 전환으로 존재하지 않는 현지 시각은 적용하지 않습니다.
  • 일출 계산은 선택한 서울 날짜의 정오를 기준으로 해당 날짜를 고정합니다. 자정에 전날의 solar day가 선택되는 것을 막습니다.

지형과 일출 시각의 차이

화면의 가는 선은 고도각 0° 기준선입니다. 실제 능선과 다릅니다. 기존 0° 아래 강제 숨김을 제거했고, 태양 원반·halo를 지형보다 먼 60km 거리에 두어 깊이 버퍼로 가립니다. 높은 곳에서는 0° 아래 태양도 보일 수 있고, 가까운 능선은 0° 위 태양도 가릴 수 있습니다.

정보 패널의 주 일출·일몰은 DSM 표면+눈높이 약 608.606m의 지평선 하강 보정을 적용한 천문 시각입니다. 보조 줄은 표준 0m 계산입니다. 능선 위로 태양이 처음 보이는 시각을 별도로 계산한 값은 아닙니다. 지형 차폐는 렌더링에 반영됩니다. 영역 밖은 단순 해수면 기준 지평선이며 실제 먼 산은 추가 자료가 필요합니다.

DEM 파일과 재현

  • 원본: data/raw/copernicus/Copernicus_DSM_COG_10_N37_00_E126_00_DEM.tif (29,862,574 bytes), ...E127...tif (49,403,795 bytes). 두 파일 모두 다운로드하여 보관했습니다.
  • 웹용: public/terrain/gwanaksan.f32 (2,566,404 bytes), gwanaksan.json (격자·좌표·높이·원본 URL·SHA256), source.html (출처/제한).
  • 원본 격자 1 arc-second, WGS84 경위도. 두 타일을 먼저 병합해 경도 127° 경계의 보간을 연결합니다.
  • 관측점 중심 AEQD 투영, 30m 간격 bilinear 재표본화, 801×801. 표면고도 범위 1.314~618.475m, 결측 0개. 새 격자 간격은 원본보다 높아진 정확도를 의미하지 않습니다.
  • +X 동 / -Z 북, 지면의 높이는 DSM 표면고도에서 관측점 표면고도를 뺍니다. 지구 곡률은 평균 지구 반지름에 대한 거리²/(2R)로 근사합니다. 지상 대기굴절·지역 지오이드 기울기는 별도 반영하지 않습니다.
  • 1,280,000개 삼각형. 색상은 높이/경사 기반이며 실제 토지피복이나 항공사진이 아닙니다. 먼 영역은 시각적 안개로 완화합니다.

일반 실행에는 Python이 필요 없습니다. 전처리를 재현할 때만 Python 3.12와 아래 패키지가 필요합니다.

python -m pip install --target .tools/terrain -r scripts/terrain-requirements.txt
python scripts/prepare-terrain.py
npm run build

전처리는 src/config/observer.ts의 좌표를 읽습니다. 원본 두 타일은 미리 다운로드되어 있어야 하며 URL은 public/terrain/gwanaksan.json에 기록되어 있습니다. 원본과 전처리 패키지는 .gitignore 대상이고, public/terrain/ 가공 데이터는 앱 배포에 필요합니다.

공식 배포 · 사용 조건 및 가공 설명

검증 결과

  • npm test: 22개 통과. 기존 18개 + 실제 격자 검증, 방위/높이/곡률/카메라 지면 정합, 합성 능선 및 실제 관악산 격자의 태양 차폐 검사.
  • npm run build: TypeScript 검사 및 production build 통과. Three.js를 포함한 단일 JS 번들이 약 548 kB(gzip 약 142 kB)로 Vite의 500 kB 권고 경고가 있습니다. 빌드 오류는 아닙니다.
  • NOAA 독립 수식 대조 최대 차이: 방위각 0.0186°, 0° 이상 태양의 고도각 0.0659°. 해당 구간 허용 오차 0.1° 이내입니다.
  • 0° 아래는 SunCalc와 NOAA 굴절 모델 차이로 최대 0.4842° 차이가 나며, 0.5° 기준으로 따로 검증합니다. 전체 구간이 0.1° 이내라는 의미는 아닙니다. 1단계에서는 음의 고도를 숨겼지만, 지형 모드에서는 그 일부가 보일 수 있으므로 이 모델 차이도 관측 일출의 오차 요인입니다.
  • 상세 결과: 태양 계산 검증표. 검증용 NOAA 코드는 tests/에만 있고 앱에서 사용하지 않습니다.
  • 실제 내장 브라우저에서 DSM 메시, DSM 높이 표시, 날짜·슬라이더 변경, 일출/낮 장면을 확인했습니다. 별도 건물 데이터는 적용하지 않았습니다.
  • 실제 격자와 태양 방향의 교차 검사: 현재 임시 관측점 기준 2026-03-20 06:40 태양 중심 차폐, 06:50 통과. 현장 실측 일출 시각을 의미하지 않습니다.
  • Pointer Lock 실제 회전/ESC 해제 검증은 미완료입니다. 내장 브라우저가 Pointer Lock을 거부했고, 일반 Chrome 검증은 자동화 도구가 현재 URL을 확인하지 못해 중단되었습니다. 일반 PC 브라우저에서 하늘 클릭 → 좌우·상하 회전 → ESC 해제를 수동 확인해야 합니다. 앱에는 잠금 실패 안내가 있습니다.

모듈

src/
  main.ts                     앱 연결
  config/observer.ts          임시 관측점
  constants/                 시간·장면 상수
  astronomy/sunCalculator.ts  SunCalc 2.x 계산
  astronomy/direction.ts      월드 방향 변환
  scene/world.ts             장면·카메라·렌더 루프
  scene/sky.ts               Sky 대기 효과
  scene/sun.ts               원반·halo·광원
  scene/horizon.ts           가상 지평선·방위 표시
  scene/terrain.ts           가공 높이 데이터 로딩·지형 재질
  scene/terrainGeometry.ts   격자 검증·좌표/곡률·메시 생성
  controls/fpsCameraController.ts  독립 FPS 시점 조작
  ui/time.ts                 서울 날짜·시각 변환
  ui/timeController.ts       LIVE/CUSTOM 제어
  ui/informationPanel.ts     정보 표시

별도 건물 모델, 캐릭터 이동·충돌, 날씨·구름 데이터, 백엔드는 구현하지 않았습니다. 하늘 색과 빛 번짐은 시각적 근사이며 실제 날씨나 광도 예측이 아닙니다. 태양 위치 검증은 계산 모델 간 대조이며 현장 실측 검증이 아닙니다.

구조물 1차 배치 (2026-10-29)

정상 마스트 1기, 남쪽 송신탑 4기, 레이돔·받침 건물, 북쪽/연주대 조망데크 2곳과 계단·난간을 추가했다. 하단 방향 버튼은 카메라 위치와 관측 시각을 유지하며 시선만 바꾼다. WASD 지형 탐색은 구현했으며 구조물 충돌·데크 위 보행은 아직 구현하지 않았다.

src/constants/structures.ts에서 위치·높이·크기·방향과 자료 근거를 보정할 수 있다. 모든 치수는 추정값이며 개별 송신탑과 데크 위치도 일부 추정이다. 기초와 지지대는 실제 렌더링되는 DSM 삼각형 표면을 샘플링해 배치한다. 지형은 편집하지 않았다. 현재 임시 관측점에서는 레이돔 하부가 능선에 가려질 수 있다. 구조물 위치·기초 표고와 근거리 지형은 추가 검증이 필요하다.

공개 안내: /structures.html. 수집 자료: data/references/summit/README.md. 사진은 모델링 참고로만 사용하며 앱 배포 자산에 포함하지 않는다. 구조물은 일반 불투명 메시의 깊이 판정으로 태양을 가리지만, 천문 일출·일몰 표시는 기존 계산을 유지한다.

과천 아파트·건물 (2026-10-29)

로그인 없는 OSM 공식 API 원본에서 추출한 1,640개 건물 외곽선을 3D로 배치했다(apartments 태그 242개). 높이 태그 223개, 층수 환산 28개, 임시 높이 1,389개를 색으로 구분한다. 공개 데이터와 근거는 /buildings/source.html, 원본과 수집 기록은 data/raw/gwacheon/에 있다.

과천 방향은 실제 관측 위치에서 시선만 돌린다. 현재 고정점에서는 가까운 능선에 도심이 가려져 과천 건물 조감을 별도로 제공한다. 조감은 기준 지면보다 180m 높은 별도 카메라로 배치를 확인하는 기능이며 실제 눈높이 관측이나 일출 검증용 시점이 아니다. 하늘 클릭 또는 다른 방향 버튼으로 실제 관측 시점에 복귀한다. 기존 관측 좌표·눈높이·천문 계산은 유지한다.

건물은 외곽선을 수직으로 올린 단순 모델이며 지붕·창문은 미재현. 주변 DSM에서 기초 표고를 추정하므로 일부 매몰·높이 오차가 남는다. 3개 병합 메시/약 3.3만 삼각형으로 렌더링하고 새로운 의존성은 추가하지 않았다.

2026-10-30 현재는 VWorld 보완 후 6,692개 건물, 3개 병합 메시, 148,488개 삼각형이다. 야간에는 창문 패턴 대신 기존 조명 맵으로 하부 벽면과 주변 지면을 밝히며, 태양 고도 -6°~5° 사이에서 서서히 소등한다. 실제 조명 위치나 차폐를 계산하는 방식은 아니다. 창문용 정점 속성을 높이 하나로 줄여 건물 기하 버퍼가 21,382,272바이트에서 16,036,704바이트로 감소했다. 이후 미사용 UV를 제거하고 위치·법선·조명 높이가 정확히 같은 정점만 공유하여 인덱스를 포함한 버퍼는 8,979,600바이트가 되었다(추가 약 44% 감소). 삼각형 순서와 각 꼭지점의 위치·법선·조명 높이, 조명 맵 및 시간별 밝기가 이전과 동일함을 대조했다. 태양의 투영 그림자는 미구현이다.

앱은 /buildings/gwacheon.baked.json과 /buildings/gwacheon.baked.bin을 읽어 메시와 조명 맵을 복원한다. 바이너리는 기하 버퍼 8,979,600바이트와 조명 맵 1,048,576바이트를 합친 10,028,176바이트다. GeoJSON 해석·기초 표고 계산·입체화·병합·정점 공유·조명 맵 계산은 앱 로딩 경로에서 제거했다. 재질과 시간별 조명 갱신 코드는 기존 생성기와 공유한다. 원본 GeoJSON은 출처 확인·재생성 및 실험 페이지용으로 보관한다.

건물/지형 데이터 또는 생성 규칙 변경 후 npm run prepare:buildings로 재생성한다. npm run check:buildings는 입력 및 출력 SHA-256을 검사하며, npm run build 전에도 자동 실행하여 오래된 파일의 배포를 막는다. LF/CRLF 차이는 입력 변경으로 취급하지 않는다. 파일이 없거나 기준 지형이 다르면 앱은 오류를 표시하며 실행 중 건물을 다시 생성하지 않는다.

로컬 Node 측정 3회에서 이미 읽은 데이터의 건물 생성은 572786ms, 저장된 버퍼의 장면 연결은 0.140.40ms였다. 다운로드·JSON 파싱·GPU 업로드·나머지 장면 생성은 제외한 수치이며 전체 앱 로딩 시간이나 FPS 측정이 아니다. 바이너리는 원본 GeoJSON보다 크므로 실제 네트워크 전송 시간도 고려해야 한다(참고 gzip 압축 크기 약 2.37MB; 서버 압축 적용 여부에 따라 다름).

건물 전송 압축: npm run prepare:buildings는 gwacheon.baked.bin.gz도 함께 생성한다. gzip level 9 기준 2,358,179바이트로 원본 대비 약 76.5% 작으며, check:buildings는 압축을 풀어 원본과 일치하는지도 검사한다. 개발 서버와 npm run preview는 gzip을 지원하는 요청에 이 파일을 Content-Encoding: gzip, Vary: Accept-Encoding으로 전송한다. 앱의 요청 URL과 로딩 코드는 동일하며 브라우저가 자동으로 압축을 해제한다. gzip 비지원 요청과 Range 요청에는 기존 원본 파일을 제공한다. GPU 버퍼 크기는 변하지 않는다.

외부 정적 호스팅은 Vite 미들웨어를 실행하지 않으므로 해당 서버/CDN의 전송 압축 설정이 별도로 필요하다. Nginx에서 사전 압축 파일을 사용할 경우 기존 server 블록에 아래 위치 설정을 병합하고 dist를 정적 루트로 제공한다(ngx_http_gzip_static_module 필요). 압축 응답 여부는 curl -I -H "Accept-Encoding: gzip" <주소>/buildings/gwacheon.baked.bin의 Content-Encoding: gzip으로 확인한다. 외부 서버 배포는 수행하지 않았다.

location = /buildings/gwacheon.baked.bin {
    gzip_static on;
    gzip_vary on;
    default_type application/octet-stream;
    add_header Cache-Control "no-cache";
}
S
Description
No description provided
Readme
115 MiB
Languages
TypeScript 84.4%
Python 10.8%
HTML 3.1%
CSS 1.4%
JavaScript 0.3%