-- 003: 선박 위치 이력 + OSINT 피드 + 위성 TLE 테이블 -- 리플레이 및 분석용 시계열 데이터 저장 SET search_path TO kcg, public; -- ═══════════════════════════════════════════ -- 선박 위치 이력 (AIS / signal-batch) -- ═══════════════════════════════════════════ CREATE TABLE IF NOT EXISTS ship_positions ( id BIGSERIAL PRIMARY KEY, mmsi VARCHAR(9) NOT NULL, name VARCHAR(128), position geometry(Point, 4326) NOT NULL, heading DOUBLE PRECISION, speed DOUBLE PRECISION, -- knots course DOUBLE PRECISION, -- COG category VARCHAR(16), -- warship, carrier, destroyer, tanker, cargo, ... flag VARCHAR(4), -- ISO 국가코드 typecode VARCHAR(16), type_desc VARCHAR(128), imo VARCHAR(16), call_sign VARCHAR(16), status VARCHAR(64), -- Under way, Anchored, ... destination VARCHAR(128), draught DOUBLE PRECISION, length DOUBLE PRECISION, width DOUBLE PRECISION, source VARCHAR(16) NOT NULL, -- spg, signal-batch, sample region VARCHAR(16) NOT NULL, -- middleeast, korea collected_at TIMESTAMP NOT NULL DEFAULT NOW(), last_seen TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_ship_pos_geom ON ship_positions USING GIST (position); CREATE INDEX IF NOT EXISTS idx_ship_pos_collected ON ship_positions (collected_at); CREATE INDEX IF NOT EXISTS idx_ship_pos_region_time ON ship_positions (region, collected_at); CREATE INDEX IF NOT EXISTS idx_ship_pos_mmsi ON ship_positions (mmsi, collected_at); -- ═══════════════════════════════════════════ -- OSINT 피드 (GDELT, Google News, CENTCOM) -- ═══════════════════════════════════════════ CREATE TABLE IF NOT EXISTS osint_feeds ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, source VARCHAR(64) NOT NULL, -- gdelt, google-news-ko, google-news-en, centcom source_url TEXT, category VARCHAR(32), -- military, oil, diplomacy, shipping, nuclear, ... language VARCHAR(8), -- ko, en focus VARCHAR(16), -- iran, korea image_url TEXT, position geometry(Point, 4326), -- nullable (위치 추출 가능 시) published_at TIMESTAMP, -- 원본 기사 발행 시각 collected_at TIMESTAMP NOT NULL DEFAULT NOW(), UNIQUE(source, source_url) -- 중복 방지 ); CREATE INDEX IF NOT EXISTS idx_osint_feeds_collected ON osint_feeds (collected_at); CREATE INDEX IF NOT EXISTS idx_osint_feeds_focus ON osint_feeds (focus, collected_at); CREATE INDEX IF NOT EXISTS idx_osint_feeds_category ON osint_feeds (category); CREATE INDEX IF NOT EXISTS idx_osint_feeds_geom ON osint_feeds USING GIST (position); -- ═══════════════════════════════════════════ -- 위성 TLE (CelesTrak) -- ═══════════════════════════════════════════ CREATE TABLE IF NOT EXISTS satellite_tle ( id BIGSERIAL PRIMARY KEY, norad_id INTEGER NOT NULL, name VARCHAR(128) NOT NULL, tle_line1 VARCHAR(70) NOT NULL, tle_line2 VARCHAR(70) NOT NULL, category VARCHAR(20), -- reconnaissance, communications, navigation, weather, other tle_group VARCHAR(32), -- military, gps-ops, geo, weather, stations epoch TIMESTAMP, -- TLE epoch (궤도 기준 시각) collected_at TIMESTAMP NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_satellite_tle_norad ON satellite_tle (norad_id, collected_at); CREATE INDEX IF NOT EXISTS idx_satellite_tle_collected ON satellite_tle (collected_at); CREATE INDEX IF NOT EXISTS idx_satellite_tle_category ON satellite_tle (category);