feat: initialize sunrise with 100-player shared rooms

This commit is contained in:
2026-10-30 16:36:35 +09:00
commit 31d6eac4c2
257 changed files with 66063 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
export const BUILDINGS = {
url: '/buildings/gwacheon.geojson',
assetManifestUrl: '/buildings/gwacheon.baked.json',
assetBufferUrl: '/buildings/gwacheon.baked.bin',
assetVersion: 1,
metersPerLevel: 3,
// Visual placeholders only: not a claim about actual floors.
fallbackHeight: { apartments: 15, residential: 9, house: 6, commercial: 12, greenhouse: 3, roof: 3, school: 12, default: 9 } as Record<string, number>,
baseSampleRadius: 45,
overviewHeight: 180,
target: { latitude: 37.429, longitude: 126.991 },
colors: { tagged: '#c5c7bf', levels: '#b4bdc1', fallback: '#a99b85' },
// Illustrative exterior lighting, not surveyed fixtures or light transport.
lighting: {
fadeStartAltitude: -6, fadeEndAltitude: 5, facadeFadeHeight: 24,
spillMapSize: 1024, spillDistance: 14, spillSpacing: 18, spillIntensity: 0.9,
bloomStrength: 0.22, bloomRadius: 0.15, bloomThreshold: 1.2,
},
} as const;
+19
View File
@@ -0,0 +1,19 @@
export const CHARACTER = {
url: '/characters/red-sheep.glb?v=rounded-v10',
scale: 0.62,
outline: 0.018,
ink: '#352724',
lighting: {
nightBrightness: 0.12,
minimumBrightness: 0.08,
nightAltitude: -8,
dayAltitude: 15,
},
waveSeconds: 3,
prayerWindowMs: 5 * 60 * 1000,
cameraDistance: 5,
cameraLift: 1.15,
cameraClearance: 0.35,
cameraTurnRate: 1.8,
turnSpeed: 4,
} as const;
+15
View File
@@ -0,0 +1,15 @@
export const COAST = {
// OSM node/12019797394: Hajodae Pavilion.
latitude: 38.020813,
longitude: 128.7352564,
radiusMeters: 1500,
spacingMeters: 10,
maskSpacingMeters: 2,
distanceRangeMeters: 32,
oceanRadiusMeters: 30000,
oceanSegments: 240,
overviewHeight: 900,
cape: { latitude: 38.021987, longitude: 128.7355911 },
// Protected-tree reference coordinates; approximate viewing bearing only.
pine: { latitude: 38 + 1.252 / 60, longitude: 128 + 44.134 / 60 },
} as const;
+5
View File
@@ -0,0 +1,5 @@
export const COMPASS = {
tickDegrees: 5,
visibleDegrees: 150,
labels: ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'],
} as const;
+15
View File
@@ -0,0 +1,15 @@
export const GISAMUN = {
latitude: 38.00555,
longitude: 128.73195,
// OSM coastline nodes spanning the beach south of Gisamun harbour.
// Sand extent is an illustrative 35m inland strip, not surveyed geometry.
sandLatitudeMin: 38.0043299,
sandLatitudeMax: 38.0064958,
sandWidthMeters: 35,
harbour: { latitude: 38.0085, longitude: 128.7328 },
// Local metres relative to the unchanged terrain-data origin. Provisional
// north breakwater entrance, not a surveyed visitor/camera location.
viewpoint: { x: -30, z: -350 },
// Centre of the mapped offshore island's bounding box (Jodo).
jodo: { x: 970.095, z: -171.101 },
} as const;
+78
View File
@@ -0,0 +1,78 @@
// Footprint follows OSM way/583161999 in the published Gisamun local coordinates.
// Inland closure, top elevation, wall height and shelter strength are illustrative.
export const GISAMUN_HARBOUR = {
topElevation: 3,
bottomElevation: -2,
shelterWaveScale: .25,
entranceBlendMeters: 18,
parapetHeight: .7,
parapetWidth: .65,
topColor: '#a2a198',
wallColor: '#737a78',
land: [
[-28.051, -155.739],
[-4.057, -150.511],
[13.586, -144.984],
[25.908, -151.71],
[47.118, -140.822],
[58.307, -147.348],
[36.956, -163.498],
[25.943, -167.849],
[-12.129, -159.935],
[-31.678, -173.088],
[-37.272, -178.882],
[-114.628, -175.93],
[-129.038, -274.496],
[-122.205, -279.746],
[-121.933, -316.042],
[-115.995, -349.851],
[-100.196, -362.937],
[-42.848, -363.359],
[-35.024, -332.835],
[-22.272, -302.31],
[-15.94, -303.764],
[-3.047, -271.731],
[16.019, -247.7],
[95.386, -163.543],
[90.485, -155.029],
[98.916, -150.723],
[111.818, -152.21],
[118.158, -165.841],
[56.804, -238.465],
[21.244, -279.578],
[-4.857, -330.848],
[-17.424, -401.508],
[-24.81, -423.552],
[-180, -423.552],
[-180, -155.739],
],
basin: [
[58.307, -147.348],
[36.956, -163.498],
[25.943, -167.849],
[-12.129, -159.935],
[-31.678, -173.088],
[-37.272, -178.882],
[-114.628, -175.93],
[-129.038, -274.496],
[-122.205, -279.746],
[-121.933, -316.042],
[-115.995, -349.851],
[-100.196, -362.937],
[-42.848, -363.359],
[-35.024, -332.835],
[-22.272, -302.31],
[-15.94, -303.764],
[-3.047, -271.731],
[16.019, -247.7],
[95.386, -163.543],
],
parapet: [
[118.158, -165.841],
[56.804, -238.465],
[21.244, -279.578],
[-4.857, -330.848],
[-17.424, -401.508],
[-24.81, -423.552],
],
} as const;
+6
View File
@@ -0,0 +1,6 @@
export const IMPOSTOR_EXPERIMENT = {
width: 1280, height: 720, textureScale: 2, sideOffset: 300,
minLongitude: 126.99, maxLongitude: 127.00,
minLatitude: 37.425, maxLatitude: 37.435,
warmupFrames: 30, sampleFrames: 90, rounds: 3,
} as const;
+35
View File
@@ -0,0 +1,35 @@
import { LOCATION_ENABLED } from './locations.ts';
export const APP_NAME = '해보러가기';
export const LOBBY_CHARACTER = {
maxSize: 360,
widthFraction: 0.65,
heightFraction: 0.55,
marginFraction: 0,
heading: 160,
frameBottom: 0.75,
frameTop: 1.5,
minimumLightingAltitude: -3,
} as const;
export const LOBBY_SUNRISE = {
durationMs: 4500,
nightHoldMs: 300,
from: Date.parse('2026-11-06T05:40:00+09:00'),
to: Date.parse('2026-11-06T07:45:00+09:00'),
heading: 110,
pitch: 12,
fieldOfView: 40,
maxPixelRatio: 1.5,
maxExposure: 0.32,
} as const;
export const LOBBY_SERVERS = [
{ id: 'gwanaksan', name: '관악산 가기', description: '능선 위에서 함께 맞이하는 아침', enabled: LOCATION_ENABLED.gwanaksan },
{ id: 'hajodae', name: '하조대 가기', description: '바다와 소나무 사이로 떠오르는 해', enabled: LOCATION_ENABLED.hajodae },
{ id: 'gisamun', name: '기사문 해변 가기', description: '잔잔한 해변에서 기다리는 첫 햇살', enabled: LOCATION_ENABLED.gisamun },
] as const;
export const LOBBY = { endpoint: '/api/lobby', timeoutMs: 10000, channelName: '채널 1' } as const;
export interface LobbyData {
servers: typeof LOBBY_SERVERS;
channel: { id: string; name: string; capacity: number; players: number; available: boolean };
}
+31
View File
@@ -0,0 +1,31 @@
import { observer, observerName } from '../config/observer.ts';
import { COAST } from './coast.ts';
import { TERRAIN } from './terrain.ts';
import { GISAMUN } from './gisamun.ts';
export const LOCATIONS = {
gwanaksan: {
name: observerName, label: 'GWANAKSAN', observer,
terrain: { metadataUrl: TERRAIN.metadataUrl, heightsUrl: TERRAIN.heightsUrl },
coast: null,
},
hajodae: {
name: '하조대 정자', label: 'HAJODAE',
observer: { latitude: COAST.latitude, longitude: COAST.longitude, elevation: 0, eyeHeight: 1.7 },
terrain: { metadataUrl: '/terrain/hajodae/metadata.json', heightsUrl: '/terrain/hajodae/heights.f32' },
coast: { directory: '/terrain/hajodae', lookTarget: COAST.pine, lookLabel: '소나무 방향' },
},
gisamun: {
name: '기사문 해변', label: 'GISAMUN',
observer: { latitude: GISAMUN.latitude, longitude: GISAMUN.longitude, elevation: 0, eyeHeight: 1.7 },
terrain: { metadataUrl: '/terrain/gisamun/metadata.json', heightsUrl: '/terrain/gisamun/heights.f32' },
coast: { directory: '/terrain/gisamun', lookTarget: GISAMUN.harbour, lookLabel: '기사문항 방향' },
},
} as const;
export const DEFAULT_LOCATION: keyof typeof LOCATIONS = 'gwanaksan';
export const LOCATION_ENABLED: Record<keyof typeof LOCATIONS, boolean> = {
gwanaksan: true,
hajodae: false,
gisamun: false,
};
+13
View File
@@ -0,0 +1,13 @@
export const MINIMAP = {
size: 256,
padding: 12,
contourInterval: 50,
majorInterval: 100,
minZoom: 1,
maxZoom: 16,
zoomStep: 2,
background: '#17272b',
contourColor: '#80998b',
majorColor: '#bed0ad',
markerColor: '#ffd29a',
} as const;
+1
View File
@@ -0,0 +1 @@
export const MOVEMENT = { speed: 4, maxDelta: 0.05, dragSensitivity: 0.003, dragThreshold: 6, arrivalDistance: 0.01 } as const;
+15
View File
@@ -0,0 +1,15 @@
export const MULTIPLAYER = {
locationId: 'gwanaksan',
roomId: 'gwanaksan-001',
capacity: 100,
tickMs: 100,
snapshotMs: 5000,
clockSyncMs: 30000,
visitHeartbeatMs: 60000,
interpolationMs: 150,
playerHeartbeatMs: 1000,
staleMovementMs: 1500,
maxPacketBytes: 4096,
ackTimeoutMs: 10000,
maxNicknameLength: 20,
} as const;
+13
View File
@@ -0,0 +1,13 @@
export const NIGHT_SKY = {
starCount: 1800,
seed: 73021,
distance: 60000,
moonRadiusKm: 1737.4,
fadeStartAltitude: -12,
fadeEndAltitude: -4,
backgroundRadiance: [0.08, 0.13, 0.22],
starRadiance: 8,
moonRadiance: 7,
moonFadeStartAltitude: -4,
moonFadeEndAltitude: 5,
} as const;
+5
View File
@@ -0,0 +1,5 @@
export const EYE_HEIGHT = {
minCentimeters: 160,
maxCentimeters: 195,
stepCentimeters: 1,
} as const;
+9
View File
@@ -0,0 +1,9 @@
/** Visual density limits, not meteorological severity thresholds. */
export const PRECIPITATION = {
radius: 22,
height: 30,
belowCamera: 10,
maxDelta: 0.05,
rain: { maxParticles: 5000, fullRate: 30, speed: 14, streak: 0.65, opacity: 0.28 },
snow: { maxParticles: 2200, fullRate: 4, speed: 1.2, size: 0.09, opacity: 0.85, drift: 0.35 },
} as const;
+4
View File
@@ -0,0 +1,4 @@
export const RAIN_GEAR = {
umbrellaUrl: '/characters/umbrella.glb?v=1',
raincoatUrl: '/characters/raincoat.glb?v=5',
} as const;
+12
View File
@@ -0,0 +1,12 @@
export const RENDER_QUALITY = {
mobileCap: 1.5,
mobilePixelBudget: 1_500_000,
levels: [0.75, 1, 1.25, 1.5, 1.75, 2] as readonly number[],
settleMs: 10_000,
windowMs: 5_000,
minimumSamples: 30,
lowerP95Ms: 34,
raiseP95Ms: 22,
raiseMeanMs: 19,
healthyWindows: 4,
} as const;
+20
View File
@@ -0,0 +1,20 @@
export const SCENE = {
fieldOfView: 65,
near: 0.1,
far: 100000,
skyRadius: 45000,
sunDistance: 60000,
sunDiameterDegrees: 0.53,
haloDiameterDegrees: 7,
haloOpacity: 0.32,
sunRadianceAtHorizon: 6,
sunRadianceAtDay: 40,
sunRadianceDayAltitude: 15,
mieCoefficient: 0.005,
mieDirectionalG: 0.8,
horizonRadius: 40000,
horizonBlendDegrees: 2,
markerRadius: 3000,
pitchLimitDegrees: 85,
maxPixelRatio: 2,
} as const;
+24
View File
@@ -0,0 +1,24 @@
export const STRUCTURE_ASSETS = {
baseUrl: '/models/summit',
foundationColor: '#bbb9ac',
supportColor: '#454b42',
access: { deckIds: ['north-deck'] as readonly string[], treadDepth: .3, maxRiser: .18, maxSteps: 32, landingLength: .6, groundOverlap: .1 },
} as const;
/** Preliminary dimensions in meters; NOT surveyed heights. Sources: data/references/summit. */
export const STRUCTURES = {
towers: [
{ id: 'summit-mast', latitude: 37.4450675, longitude: 126.9638043, height: 24, width: 2.4, source: 'OSM node/3445797250', estimatedPosition: false },
// OSM transmitter points identify facilities, not the centers of individual towers.
{ id: 'south-tower-1', latitude: 37.4401643, longitude: 126.9642555, height: 48, width: 5, source: 'OSM node/4262612712 + recent-02.jpg', estimatedPosition: true },
{ id: 'south-tower-2', latitude: 37.4400359, longitude: 126.9640016, height: 36, width: 3.2, source: 'OSM node/4736107159 + recent-02.jpg', estimatedPosition: true },
{ id: 'south-tower-3', latitude: 37.44012, longitude: 126.96448, height: 40, width: 3, source: 'recent-02.jpg; approximate cluster offset', estimatedPosition: true },
{ id: 'south-tower-4', latitude: 37.44003, longitude: 126.96472, height: 34, width: 2.4, source: 'recent-02.jpg; approximate cluster offset', estimatedPosition: true },
],
radar: { latitude: 37.444028, longitude: 126.963808, radius: 4, buildingHeight: 7, source: 'OSM way/337498781 (identity inferred), photo-01/09/10' },
decks: [
{ id: 'north-deck', enabled: false, latitude: 37.44516, longitude: 126.96405, width: 3, length: 7, heading: -20, stairLength: 7, stairDrop: 3, source: 'deck-19/20/22; position and dimensions estimated' },
].filter(deck => deck.enabled),
railing: { height: 1.15, postWidth: 0.1, maxSpan: 1.5 },
deck: { thickness: 0.18, clearance: 0.25, plankWidth: 0.18 },
} as const;
+9
View File
@@ -0,0 +1,9 @@
export const SUMMIT_ASSETS = {
manifestUrl: '/terrain/summit-mesh.json',
bufferUrl: '/terrain/summit-mesh.bin',
version: 1,
magic: 0x53554d54,
interiorMaxEdge: 5.6,
detailedStartRadius: 20,
attributes: [['position',3],['normal',3],['uv',2],['color',3],['trailDistance',1],['rockDistance',1]] as const,
};
+10
View File
@@ -0,0 +1,10 @@
/** Illustrative CC0 surface scans; not photographs or measured materials of this site. */
export const SUMMIT_MATERIALS = {
baseUrl: '/terrain/materials',
anisotropy: 8,
edgeBlend: { innerMeters: -0.5, outerMeters: 0.4, irregularityMeters: 0.16 },
rockBlend: { innerMeters: -0.7, outerMeters: 0.5, irregularityMeters: 0.25 },
path: { tileMeters: 2, normalStrength: 0.65, tint: '#ffffff' },
rock: { tileMeters: 1.8, normalStrength: 0.55, tint: '#ffffff', secondaryScale: 1.37, secondaryOffset: [0.31, 0.73], secondaryBlend: 0.5, secondaryVariation: 0.3 },
forest: { tileMeters: 2, normalStrength: 0.75, tint: '#b8c6a3', farColor: '#697052', fadeNear: 15, fadeFar: 65 },
} as const;
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
/** User reference photos, 2026-10-30. Visual estimates, not surveyed rock geometry.
* Offsets in metres east/south of the radar; tops relative to its terrain contact.
* The adjacent mast in the first photo is not identified with the mapped north mast.
*/
export const SUMMIT_RELIEF = {
rockTint: '#ffffff',
assetId: 'radar-rocks',
access: { bottom: [-10, -28], top: [-8, -4], landingEnd: [-3.5, -4], width: 1.25, treadDepth: .28, railHeight: 1.05, railRadius: .035, postEvery: 5, color: '#85999c' },
} as const;
+33
View File
@@ -0,0 +1,33 @@
/** Photo/contour interpretation, NOT surveyed GPS or elevations. See /terrain/summit-reconstruction.html. */
export const SUMMIT_TERRAIN = {
start: { latitude: 37.44472, longitude: 126.96422 },
coreRadius: 100,
outerRadius: 200,
// Looking south leaves the radar to the right, the eastern valley to the left.
heading: 190,
pitch: -3,
controls: [
{ label: '시작 위치 암반', latitude: 37.44472, longitude: 126.96422, elevation: 620 },
{ label: '북쪽 정상 암반', latitude: 37.44503, longitude: 126.96394, elevation: 629 },
{ label: '정상 표지석 일대 암반', latitude: 37.4451398, longitude: 126.9642379, elevation: 629 },
{ label: '정상에서 내려오는 넓은 석판', latitude: 37.44494, longitude: 126.96420, elevation: 626 },
{ label: '중간 안부', latitude: 37.44440, longitude: 126.96402, elevation: 606 },
{ label: '레이돔 암봉', latitude: 37.444028, longitude: 126.963808, elevation: 628 },
// User photos: narrow radar crown, steep exposed faces, separate western rock fin.
{ label: '레이돔 북쪽 받침', latitude: 37.444080, longitude: 126.963808, elevation: 628 },
{ label: '레이돔 남쪽 받침', latitude: 37.443975, longitude: 126.963808, elevation: 627 },
{ label: '레이돔 동쪽 받침', latitude: 37.444028, longitude: 126.963875, elevation: 627 },
{ label: '레이돔 서쪽 받침', latitude: 37.444028, longitude: 126.963740, elevation: 627 },
{ label: '북동 절벽 하단', latitude: 37.444145, longitude: 126.963945, elevation: 603 },
{ label: '북쪽 절벽 하단', latitude: 37.444185, longitude: 126.963805, elevation: 608 },
{ label: '서쪽 첨봉 받침', latitude: 37.444105, longitude: 126.963580, elevation: 617 },
{ label: '첨봉 북쪽 암릉', latitude: 37.444240, longitude: 126.963570, elevation: 608 },
{ label: '첨봉 아래 안부', latitude: 37.444380, longitude: 126.963680, elevation: 603 },
{ label: '암봉 북사면', latitude: 37.44425, longitude: 126.96390, elevation: 610 },
{ label: '암봉 동사면', latitude: 37.44403, longitude: 126.96408, elevation: 605 },
{ label: '암봉 서사면', latitude: 37.44403, longitude: 126.96353, elevation: 604 },
{ label: '암봉 남사면', latitude: 37.44382, longitude: 126.96382, elevation: 610 },
{ label: '동쪽 골짜기', latitude: 37.44435, longitude: 126.96465, elevation: 582 },
{ label: '서쪽 사면', latitude: 37.44435, longitude: 126.96335, elevation: 582 },
],
} as const;
+343
View File
@@ -0,0 +1,343 @@
/** OSM centerlines; widths and surface boundaries estimated from Naver Oct 2013 panoramas.
* No public access inferred for the radar facility spur (way/337498785).
*/
import { SUMMIT_PLATEAU } from './summitPlateau.ts';
export const SUMMIT_TRAILS = {
radius: 200, surfaceStep: 0.7, surfaceLift: 0.06, sweepStep: 0.2,
mapZoom: 64, mapMaxZoom: 128,
colors: { path: '#b5a080', rock: '#a7a397' },
// Photo-estimated slab from the lower forecourt to the OSM summit point ~47m north.
// Shared by walking, rock material and minimap; excludes the western mast and eastern drop.
rockPolygon: SUMMIT_PLATEAU.boundary,
rockOutcrops: [
{ latitude: 37.444028, longitude: 126.963808, radius: 14 },
{ latitude: 37.444105, longitude: 126.963580, radius: 11 },
{ latitude: 37.444240, longitude: 126.963570, radius: 9 },
],
routes: [
{
"id": "way/337498789",
"label": "정상 암반길",
"width": 3.2,
"points": [
[
126.9642427,
37.4452121
],
[
126.9642346,
37.4450861
],
[
126.9641628,
37.4449609
],
[
126.9641194,
37.4449084
],
[
126.9641086,
37.4448542
],
[
126.9641235,
37.4447614
],
[
126.9641299,
37.4446834
],
[
126.9641627,
37.4446443
],
[
126.9641937,
37.4446176
],
[
126.9641853,
37.4445946
]
]
},
{
"id": "way/37751526",
"label": "남쪽 하산 계단길",
"width": 2.4,
"points": [
[
126.9652191,
37.4426477
],
[
126.965157,
37.4427111
],
[
126.9650832,
37.4428437
],
[
126.9649758,
37.4428823
],
[
126.9646903,
37.4431718
],
[
126.9644942,
37.4433977
],
[
126.9645363,
37.4435795
],
[
126.9644974,
37.4437541
],
[
126.9645055,
37.4438755
],
[
126.9644849,
37.4439916
],
[
126.9644634,
37.4441563
],
[
126.9644231,
37.4442014
],
[
126.9643909,
37.4441957
],
[
126.9643509,
37.4442247
],
[
126.9642731,
37.4442455
],
[
126.9642271,
37.4442888
],
[
126.9641796,
37.4443334
],
[
126.9641677,
37.4443904
],
[
126.9641998,
37.4444216
],
[
126.9642101,
37.444477
],
[
126.9641853,
37.4445946
]
]
},
{
"id": "way/337498787",
"label": "동쪽 계단길",
"width": 2.2,
"points": [
[
126.9646956,
37.4448264
],
[
126.9644011,
37.4449652
],
[
126.9641628,
37.4449609
]
]
},
{
"id": "way/337498788",
"label": "북쪽 능선길",
"width": 2.4,
"points": [
[
126.9652188,
37.445905
],
[
126.9651599,
37.4458421
],
[
126.9651204,
37.445817
],
[
126.9650739,
37.4458262
],
[
126.965047,
37.4458008
],
[
126.9650012,
37.4458049
],
[
126.9649836,
37.4457585
],
[
126.9649235,
37.4457452
],
[
126.9649129,
37.4458003
],
[
126.9648793,
37.445814
],
[
126.9648108,
37.4457987
],
[
126.9648084,
37.4457327
],
[
126.9647692,
37.4457201
],
[
126.9647305,
37.4457475
],
[
126.9646677,
37.4457742
],
[
126.9646443,
37.4457812
],
[
126.9646393,
37.4457277
],
[
126.9646373,
37.4456519
],
[
126.9646231,
37.4456153
],
[
126.9645478,
37.4456006
],
[
126.9645194,
37.4455309
],
[
126.9645211,
37.4455051
],
[
126.9644963,
37.4454888
],
[
126.9645078,
37.4454665
],
[
126.9644384,
37.4454786
],
[
126.9644443,
37.4454544
],
[
126.9644447,
37.4454142
],
[
126.9644298,
37.4453984
],
[
126.9644073,
37.4454045
],
[
126.9644102,
37.4454574
],
[
126.9643506,
37.445427
],
[
126.9642934,
37.4454174
],
[
126.9642857,
37.4454008
],
[
126.9643346,
37.4453985
],
[
126.9643315,
37.4453643
],
[
126.9642929,
37.4453472
],
[
126.9642727,
37.4453499
],
[
126.9642484,
37.4453197
],
[
126.9642584,
37.4452722
],
[
126.9642427,
37.4452121
]
]
}
]
} as const;
+9
View File
@@ -0,0 +1,9 @@
export const TERRAIN = {
metadataUrl: '/terrain/gwanaksan.json',
heightsUrl: '/terrain/gwanaksan.f32',
earthRadiusMeters: 6371008.8,
fogNear: 4000,
fogFar: 16500,
refinementRadiusMeters: 180,
refinementSegments: 6,
} as const;
+5
View File
@@ -0,0 +1,5 @@
export const TIME_ZONE = 'Asia/Seoul';
export const KST_OFFSET_MS = 9 * 60 * 60 * 1000;
export const LIVE_INTERVAL_MS = 1000;
export const TIME_STEP_SECONDS = 10;
export const LAST_SELECTABLE_SECOND = 86400 - TIME_STEP_SECONDS;
+15
View File
@@ -0,0 +1,15 @@
export const DIALOG_TITLES = {
menu: '메뉴', location: '장소', time: '날짜·시간', information: '관측 정보',
view: '지도·시점', session: '닉네임·접속 상태',
settings: '설정·조작 방법', sources: '출처·오픈소스',
} as const;
export const EQUIPMENT_NOTICE = {
weather: '기상악화시 사용할 수 있습니다',
raincoat: '우비를 먼저 착용해 주세요',
durationMs: 2400,
margin: 12,
pointerOffset: 16,
} as const;
export const PRAYER_NOTICE = '일출 전후 5분에 사용할 수 있습니다';
+22
View File
@@ -0,0 +1,22 @@
export const WEATHER = {
forecastUrl: 'https://api.open-meteo.com/v1/forecast',
historyUrl: 'https://historical-forecast-api.open-meteo.com/v1/forecast',
archiveUrl: 'https://archive-api.open-meteo.com/v1/archive',
archiveStart: '1940-01-01',
historyStart: '2022-01-01',
recentDays: 5,
forecastDays: 16,
hourMs: 3600000,
dayMs: 86400000,
refreshMs: 30 * 60 * 1000,
retryMs: 2 * 60 * 1000,
timeoutMs: 15000,
cacheDays: 8,
transitionSeconds: 0.7,
cloudOpticalDepth: 3.5,
hazePathMeters: 2000,
cloudScale: 3.8,
overcastStart: 0.7,
cloudOpacity: 0.98,
overcastAmbient: 0.65,
} as const;