cosmo/RAODMAP.md

13 KiB
Raw Permalink Blame History

这是一个基于我们需要实现的功能、技术选型以及数据策略整理而成的完整产品路线图Roadmap。你可以直接将此内容保存为 roadmap.md 文件,用于项目管理或开发指引。


Product Roadmap: 深空与系外行星可视化系统

1. 项目愿景 (Product Vision)

构建一个基于 Web 的 3D 可视化系统,能够精确展示太阳系内主要天体及人造探测器(如旅行者号)的实时位置,并能无缝切换至恒星际视角,探索临近的系外行星系统。系统强调科学数据的准确性(基于 NASA 数据)与视觉表现的真实感。

2. 技术架构 (Technical Architecture)

后端 (Data Processing)

  • 语言: Python 3.x
  • 核心库:
    • astroquery: 连接 NASA JPL Horizons (太阳系) 和 NASA Exoplanet Archive (系外行星)。
    • skyfield / astropy: 处理时间坐标转换、球坐标转直角坐标。
    • numpy: 矢量计算。
  • 输出: JSON 格式的静态数据或 RESTful API。

前端 (Visualization)

  • 引擎: Three.js (WebGL)
  • 核心技术:
    • OrbitControls: 视角控制。
    • GLTFLoader: 加载探测器 3D 模型。
    • LogarithmicDepthBuffer: 解决超大空间尺度下的深度闪烁问题。
  • UI 框架: Vue / React / Vanilla JS (根据喜好选择,用于覆盖层 UI)。

3. 开发路线规划 (Development Phases)

Phase 1: 太阳系核心架构 (Core Solar System)

目标: 搭建基础 3D 场景,实现太阳、八大行星及关键深空探测器的实时定位。

  • 数据层搭建 (Python)
    • 集成 astroquery.jplhorizons
    • 实现以太阳为原点 (Heliocentric) 的坐标获取脚本。
    • 确定单位标准:使用 天文单位 (AU) 作为基础单位。
    • 建立目标 ID 列表:八大行星 + Voyager 1/2 + Parker Solar Probe + New Horizons。
  • 可视化原型 (Three.js)
    • 初始化场景、相机、灯光(点光源 @ 0,0,0
    • 解析 Python 生成的 JSON 数据,放置代表天体的球体。
    • 实现基础的轨道线绘制查询过去1年到未来1年的坐标点连线
  • 探测器模型接入
    • 下载 NASA 官方 .glb 模型 (Voyager, Cassini 等)。
    • 实现 GLTFLoader 加载模型替换简单的方块/球体。

Phase 2: 视觉增强与尺度管理 (Visual Fidelity & Scale)

目标: 解决“看不见”的问题,提升渲染真实度。

  • 动态尺度系统 (Dynamic Scaling)
    • 实现根据摄像机距离动态调整物体大小的算法 (Billboard 模式)。
    • 确保远景能看到图标/放大版星球,近景自动切换为真实比例模型。
  • 纹理与材质 (Textures)
    • 应用行星高清贴图 (Diffuse + Normal + Specular)。
    • 卫星纹理策略:
      • 高清卫星 (月球, 木卫二, 土卫六): 使用真实地图。
      • 通用卫星 (小卫星): 使用程序化生成的岩石/冰块材质。
    • 特殊天体: 实现太阳的发光效果 (Bloom/Glow) 和土卫六的大气层效果。
  • 交互基础
    • 实现点击天体列表或 3D 物体,摄像机平滑飞行聚焦 (FlyTo)。

Phase 3: 恒星际扩展 (Interstellar Expansion)

目标: 跳出太阳系,展示临近恒星和系外行星概览。

  • 系外数据获取 (Python)
    • 集成 NasaExoplanetArchive
    • 筛选距离地球最近的 100-500 个恒星系。
    • 坐标转换算法:(RA, Dec, Distance) -> (x, y, z),统一转换为 秒差距 (pc) 或光年单位。
  • 银河系视图 (Galaxy View)
    • 创建新的 Three.js 场景或使用层级切换。
    • 使用 粒子系统 (PointsMaterial) 渲染恒星背景,高性能展示大量恒星。
    • 实现恒星颜色映射:根据光谱类型 (O, B, A, F, G, K, M) 赋予粒子不同颜色。
    • 交互:鼠标悬停粒子显示恒星名称及行星数量。

Phase 4: 系外行星系统详情 (Exoplanet Systems)

目标: 进入特定的系外恒星系,基于轨道参数模拟未知世界。

  • 详细轨道数据
    • 获取开普勒轨道参数:半长轴、周期、离心率、行星半径、平衡温度。
  • 轨道模拟与渲染
    • 使用 EllipseCurve 绘制椭圆轨道。
    • 基于时间戳计算行星在轨道上的近似位置。
  • 程序化星球外观 (Procedural Appearance)
    • 建立通用纹理库 (Gas Giant, Rocky, Ice, Lava)。
    • 编写匹配逻辑:根据 行星半径温度 自动分配材质和颜色。
      • Example: 半径 > 4 Earths -> 气态巨行星纹理。
      • Example: 温度 > 400K -> 熔岩纹理。

4. 资源清单 (Resources)

数据源 (Data Sources)

美术资产 (Assets)

关键 ID 参考 (Horizons ID)

  • Sun: @sun
  • Voyager 1: -31
  • Voyager 2: -32
  • New Horizons: -98
  • Parker Solar Probe: -96
  • Mars: 499
  • Jupiter: 599

5. 待解决难点与风险 (Risks & Challenges)

  1. 坐标系融合: 太阳系 (AU) 与恒星际 (pc/LightYear) 跨度过大。
    • Solution: 采用场景分割 (Scene Switching) 或对数深度缓冲 (Logarithmic Depth Buffer)。
  2. 数据缺失: 许多系外行星缺失半径或温度数据。
    • Solution: 在后端脚本中设置合理的默认值 (Default Fallback),防止前端崩溃。
  3. 性能优化: 粒子数量过多或模型面数过高。
    • Solution: 使用 InstancedMesh针对小天体使用低模 (Low-poly) + 法线贴图。

6. 下一步行动 (Next Steps)

  1. 运行 Python 脚本,生成 solar_system_data.jsonnearest_stars.json
  2. 搭建 Three.js 基础工程,先把太阳和地球画出来。
  3. 下载旅行者号模型,尝试加载到场景中。

这是一个为您准备好的 roadmap.md 附录补充内容。为了保持文档整洁,我将 Phase 3 和 Phase 4 的核心逻辑代码整理为了一个 "Appendix: Key Technical Implementation" 章节。

您可以直接将以下内容复制并粘贴到之前 roadmap.md 文件的底部。


Appendix: Key Technical Implementation (Phase 3 & 4)

本附录收录了实现恒星际视图Phase 3和系外行星详情页Phase 4的核心代码片段涵盖数据获取与前端渲染逻辑。

Phase 3: Interstellar View (恒星际视图)

1. Backend: 获取临近恒星并转换坐标 (Python)

此脚本筛选距离地球最近的恒星,并将天球坐标 (RA/Dec) 转换为笛卡尔坐标 (X/Y/Z)。

# get_nearest_stars.py
from astroquery.ipac.nexsci.nasa_exoplanet_archive import NasaExoplanetArchive
from astropy.coordinates import SkyCoord
from astropy import units as u
import json

def fetch_nearest_stars(limit=1000):
    # 1. 查询 NASA Exoplanet Archive (PS Table)
    # 筛选条件:距离 < 100 pc (约326光年)
    table = NasaExoplanetArchive.query_criteria(
        table="ps",
        select="hostname, sy_dist, ra, dec, sy_pnum",
        where="sy_dist < 100", 
        order="sy_dist"
    )

    unique_stars = {}
    for row in table:
        host_name = str(row['hostname'])
        if host_name in unique_stars: continue

        # 2. 坐标转换 (Spherical -> Cartesian)
        # 核心逻辑:利用 Astropy 将 RA/Dec/Distance 转为 X/Y/Z
        dist = float(row['sy_dist'])
        coord = SkyCoord(ra=float(row['ra'])*u.deg, dec=float(row['dec'])*u.deg, distance=dist*u.pc)
        
        unique_stars[host_name] = {
            "name": host_name,
            "distance_pc": dist,
            "planet_count": int(row['sy_pnum']),
            "pos": {
                "x": round(coord.cartesian.x.value, 3),
                "y": round(coord.cartesian.y.value, 3),
                "z": round(coord.cartesian.z.value, 3)
            }
        }
        if len(unique_stars) >= limit: break

    # 输出 JSON
    return list(unique_stars.values())

2. Frontend: 高性能星空渲染 (Three.js)

使用 Points (粒子系统) 而非 Mesh 来渲染数千颗恒星,保证高性能。

// StarField.js
function createStarField(starData) {
    const geometry = new THREE.BufferGeometry();
    const positions = [];
    const sizes = []; // 可选:根据恒星大小或距离调整粒子大小

    const SCALE_FACTOR = 10; // 视觉缩放系数,避免坐标过于拥挤

    starData.forEach(star => {
        positions.push(
            star.pos.x * SCALE_FACTOR,
            star.pos.y * SCALE_FACTOR,
            star.pos.z * SCALE_FACTOR
        );
        sizes.push(1.0); // 默认大小
    });

    geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
    
    // 使用 PointsMaterial
    const material = new THREE.PointsMaterial({
        color: 0xffffff,
        size: 0.5,
        sizeAttenuation: true // 开启透视消隐 (远小近大)
    });

    const starSystem = new THREE.Points(geometry, material);
    return starSystem;
}

Phase 4: Exoplanet Systems (系外行星详情)

1. Backend: 获取详细轨道参数 (Python)

获取开普勒轨道要素,用于前端绘制椭圆轨道和模拟运动。

# get_system_details.py
def fetch_system_details(hostname):
    # 查询指定恒星系的所有行星参数
    # pl_orbsmax: 半长轴 (AU), pl_orbper: 周期 (Days), pl_orbeccen: 离心率
    # pl_rade: 半径 (Earth Radii), pl_eqt: 平衡温度 (K)
    table = NasaExoplanetArchive.query_criteria(
        table="ps",
        select="pl_name, pl_orbsmax, pl_orbper, pl_orbeccen, pl_rade, pl_eqt",
        where=f"hostname = '{hostname}'"
    )
    
    planets = []
    for row in table:
        planets.append({
            "name": str(row['pl_name']),
            "a": float(row['pl_orbsmax']) if row['pl_orbsmax'] else 0.1, # Semi-major axis
            "e": float(row['pl_orbeccen']) if row['pl_orbeccen'] else 0.0, # Eccentricity
            "period": float(row['pl_orbper']) if row['pl_orbper'] else 365.0,
            "radius": float(row['pl_rade']) if row['pl_rade'] else 1.0,
            "temp": float(row['pl_eqt']) if row['pl_eqt'] else 300
        })
    return planets

2. Frontend: 轨道绘制与材质程序化匹配 (Three.js)

A. 绘制椭圆轨道:

function createOrbit(a, e) {
    // a: 半长轴 (AU), e: 离心率
    // EllipseCurve 参数: xRadius, yRadius (b = a * sqrt(1-e^2))
    const b = a * Math.sqrt(1 - (e * e));
    
    const curve = new THREE.EllipseCurve(
        0, 0,            // 中心 X, Y
        a, b,            // X半径, Y半径
        0, 2 * Math.PI   // 0 到 360度
    );
    
    const points = curve.getPoints(128);
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const material = new THREE.LineBasicMaterial({ color: 0x444444 });
    
    const orbit = new THREE.Line(geometry, material);
    
    // 【关键】偏移修正:将椭圆焦点对齐到恒星位置 (0,0)
    // 偏移量 c = a * e
    orbit.position.x = - (a * e); 
    
    // 旋转 90度让其平躺在 XZ 平面上
    orbit.rotation.x = -Math.PI / 2;
    return orbit;
}

B. 材质程序化匹配 (Procedural Texturing): 根据数据猜测行星长什么样。

// 简单的外观推断逻辑
function getPlanetTexture(radius, temp) {
    const PATH = 'assets/textures/generic/';
    
    // 1. 气态巨行星 (半径 > 4倍地球)
    if (radius > 4.0) {
        return textureLoader.load(PATH + 'gas_giant_bw.jpg'); // 配合 color 属性染色
    }
    
    // 2. 熔岩行星 (温度 > 400K)
    if (temp > 400) {
        return textureLoader.load(PATH + 'magma.jpg');
    }
    
    // 3. 冰冻星球 (温度 < 150K)
    if (temp < 150) {
        return textureLoader.load(PATH + 'ice.jpg');
    }
    
    // 4. 宜居带/岩石行星 (默认)
    return textureLoader.load(PATH + 'rocky_atmosphere.jpg');
}