feat: Heritage Globe - 中国文物全球分布地图

- Next.js + MapLibre GL 3D 地球
- CARTO dark-matter vector 底图
- 动态星空背景(闪烁、漂移、流星)
- 经纬网格线(graticule)
- 文物数据图层与朝代/类别筛选
- 中国风信息弹窗
- 中英双语 i18n
- LayerPanel + MapControls 浮动面板
This commit is contained in:
freedakgmail
2026-07-01 01:37:18 +08:00
commit a550d6d0fd
35 changed files with 11760 additions and 0 deletions
+416
View File
@@ -0,0 +1,416 @@
# Heritage Globe - 中国文物全球分布地图
## 项目概述
基于 OpenGridWorks 技术架构,构建一个展示中国流散文物全球分布的交互式地图系统。
## 技术栈
### 核心框架
- **Next.js 15** - React 服务端渲染框架
- **TypeScript** - 类型安全
- **Tailwind CSS** - 样式系统
### 地图引擎
- **MapLibre GL JS 4.x** - 开源矢量地图引擎(Mapbox GL 的开源替代)
- **PMTiles** - 云原生瓦片格式,支持按需加载
### 数据层
- **GeoJSON** - 文物坐标数据格式
- **Tippecanoe** - GeoJSON → PMTiles 转换工具
### UI 组件
- **Radix UI** - 无障碍组件库(对话框、下拉菜单等)
- **Lucide React** - 图标库
### 国际化
- **next-intl** - Next.js 国际化解决方案
- 支持中文(简体)/ English 双语切换
- URL 路径自动添加语言前缀(/zh、/en)
## 项目结构
```
heritage-globe/
├── app/ # Next.js App Router
│ ├── [locale]/ # 国际化路由
│ │ ├── layout.tsx # 语言布局
│ │ └── page.tsx # 首页(地图页)
│ ├── layout.tsx # 根布局
│ └── globals.css # 全局样式
├── messages/ # 翻译文件
│ ├── zh.json # 中文翻译
│ └── en.json # 英文翻译
├── components/ # React 组件
│ ├── map/ # 地图相关组件
│ │ ├── MapContainer.tsx # 地图容器
│ │ ├── LayerControl.tsx # 图层控制
│ │ └── RelicMarker.tsx # 文物标记
│ ├── sidebar/ # 侧边栏组件
│ │ ├── Sidebar.tsx # 侧边栏容器
│ │ ├── FilterPanel.tsx # 筛选面板
│ │ └── SearchBox.tsx # 搜索框
│ └── ui/ # 通用 UI 组件
├── lib/ # 工具函数
│ ├── map-utils.ts # 地图工具函数
│ ├── pmtiles-loader.ts # PMTiles 加载器
│ └── data-processor.ts # 数据处理
├── public/ # 静态资源
│ ├── tiles/ # PMTiles 瓦片文件
│ └── icons/ # 自定义图标
├── data/ # 原始数据
│ └── relics.geojson # 文物 GeoJSON 数据
└── types/ # TypeScript 类型定义
└── relic.ts
```
## 搭建步骤
### 阶段 1: 项目初始化
```bash
# 创建 Next.js 项目
npx create-next-app@latest . --typescript --tailwind --app --no-src-dir --import-alias "@/*"
# 安装核心依赖
npm install maplibre-gl pmtiles
# 安装 UI 组件
npm install @radix-ui/react-dialog @radix-ui/react-select @radix-ui/react-slider
npm install lucide-react
# 安装国际化
npm install next-intl
# 安装开发工具
npm install -D @types/geojson
```
### 阶段 2: 国际化配置
**2.1 创建翻译文件**
`messages/zh.json`:
```json
{
"nav": {
"title": "中国文物全球分布",
"search": "搜索文物或博物馆"
},
"map": {
"layers": "图层",
"filters": "筛选器",
"reset": "重置"
},
"dynasty": {
"shang": "商周",
"qin": "秦汉",
"tang": "隋唐",
"song": "宋元",
"ming": "明清"
},
"category": {
"bronze": "青铜器",
"porcelain": "瓷器",
"painting": "书画",
"jade": "玉器"
}
}
```
`messages/en.json`:
```json
{
"nav": {
"title": "Chinese Cultural Relics Worldwide",
"search": "Search relics or museums"
},
"map": {
"layers": "Layers",
"filters": "Filters",
"reset": "Reset"
},
"dynasty": {
"shang": "Shang-Zhou",
"qin": "Qin-Han",
"tang": "Sui-Tang",
"song": "Song-Yuan",
"ming": "Ming-Qing"
},
"category": {
"bronze": "Bronze",
"porcelain": "Porcelain",
"painting": "Painting",
"jade": "Jade"
}
}
```
**2.2 配置 next-intl**
`i18n.ts`:
```typescript
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`./messages/${locale}.json`)).default
}));
```
`middleware.ts`:
```typescript
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['zh', 'en'],
defaultLocale: 'zh',
localeDetection: true
});
export const config = {
matcher: ['/', '/(zh|en)/:path*']
};
```
### 阶段 3: 地图基础设施
**3.1 配置 MapLibre GL CSS**
-`app/[locale]/layout.tsx` 中引入 `maplibre-gl/dist/maplibre-gl.css`
**3.2 创建地图容器组件**
- `components/map/MapContainer.tsx`
- 初始化 MapLibre 地图实例
- 设置中国为初始中心点(北京:116.4074, 39.9042
- 使用 Carto 深色底图
**3.3 创建语言切换器**
```typescript
// components/LanguageSwitcher.tsx
'use client';
import { useLocale } from 'next-intl';
import { useRouter, usePathname } from 'next/navigation';
export function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const toggleLanguage = () => {
const newLocale = locale === 'zh' ? 'en' : 'zh';
router.push(pathname.replace(`/${locale}`, `/${newLocale}`));
};
return (
<button onClick={toggleLanguage}>
{locale === 'zh' ? 'EN' : '中文'}
</button>
);
}
```
**3.4 集成 PMTiles**
- `lib/pmtiles-loader.ts`
- 实现 PMTiles Protocol 注册
- 支持本地和远程瓦片加载
### 阶段 4: 数据层
**4.1 定义数据结构(支持双语)**
```typescript
// types/relic.ts
interface Relic {
id: string;
name: {
zh: string; // 中文名称
en: string; // 英文名称
};
dynasty: string; // 朝代(使用 key,如 "tang"
category: string; // 类别(使用 key,如 "bronze"
currentLocation: {
zh: string; // 中文地点
en: string; // 英文地点
};
museum: {
zh: string; // 中文机构名
en: string; // 英文机构名
};
coordinates: [number, number]; // 经纬度
year?: string; // 年代
description?: {
zh: string;
en: string;
};
imageUrl?: string; // 图片 URL
protectionLevel?: string; // 保护级别
}
```
**4.2 准备示例数据(双语)**
- 创建 `data/relics.geojson`
- 至少包含 10-20 个示例文物点位
- 覆盖英国、法国、美国、日本等主要国家
**4.3 生成 PMTiles**
```bash
# 安装 tippecanoemacOS
brew install tippecanoe
# 转换 GeoJSON → PMTiles
tippecanoe -o public/tiles/relics.pmtiles \
--minimum-zoom=0 \
--maximum-zoom=14 \
--drop-densest-as-needed \
--extend-zooms-if-still-dropping \
data/relics.geojson
```
### 阶段 5: 图层系统
**4.1 朝代图层**
- 根据朝代(商周、秦汉、隋唐、宋元、明清等)分层
- 不同朝代使用不同颜色标识
- 支持单独开关
**4.2 类别图层**
- 青铜器、瓷器、书画、玉器、石刻等
- 使用不同图标表示
**4.3 气泡样式**
- 根据文物重要性(保护级别)调整大小
- 聚类显示(缩小时自动聚合)
### 阶段 6: 交互功能
**5.1 侧边栏控制面板**
- 图层树形结构(类似 OpenGridWorks
- 拖拽排序
- 开关动画
**5.2 筛选器**
- 朝代范围滑块
- 类别多选
- 保护级别筛选
- 国家/地区筛选
**5.3 搜索功能**
- 文物名称模糊搜索
- 收藏机构搜索
- 搜索结果定位到地图
**5.4 详情弹窗**
- 点击文物标记显示详情卡片
- 展示图片、名称、年代、描述等
- 提供外部链接(如博物馆官网)
### 阶段 7: 视觉优化
**6.1 主题样式**
- 采用深色主题(类似 OpenGridWorks
- 定义 CSS 变量统一配色
**6.2 响应式设计**
- 桌面端:侧边栏固定
- 移动端:抽屉式侧边栏
**6.3 动画效果**
- 图层切换动画
- 标记 hover 效果
- 平滑缩放过渡
### 阶段 8: 高级功能(可选)
**7.1 时间轴**
- 按文物流失年代播放动画
- 展示流散历程
**7.2 统计面板**
- 总文物数量
- 分布国家统计
- 朝代占比图表
**7.3 分享功能**
- 生成带参数的分享链接
- 截图导出
**7.4 预设视图**
- "丝绸之路文物"
- "战争掠夺文物"
- "敦煌流散文物"
## 核心配置文件
### `next.config.js`
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
// 支持 PMTiles 二进制文件
config.module.rules.push({
test: /\.pmtiles$/,
type: 'asset/resource',
});
return config;
},
};
module.exports = nextConfig;
```
### `tailwind.config.ts`
```typescript
import type { Config } from 'tailwindcss';
const config: Config = {
darkMode: 'class',
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
map: {
bg: '#0a0e27',
panel: '#1a1e3a',
accent: '#3b82f6',
},
},
},
},
plugins: [],
};
export default config;
```
## 开发流程
1. **初始化项目** → 安装依赖
2. **搭建地图** → 验证 MapLibre 正常渲染
3. **加载数据** → 展示示例文物点位
4. **实现图层** → 按朝代/类别分层
5. **添加交互** → 点击、筛选、搜索
6. **优化样式** → 深色主题、响应式
7. **测试部署** → Vercel 一键部署
## 数据来源建议
- **公开数据集**: UNESCO、大英博物馆、卢浮宫等开放数据
- **研究报告**: 流失文物统计报告
- **众包贡献**: 社区上传补充
## 参考资源
- [MapLibre GL JS 文档](https://maplibre.org/maplibre-gl-js/docs/)
- [PMTiles 规范](https://github.com/protomaps/PMTiles)
- [OpenGridWorks](https://opengridworks.com) - 参考实现
- [GeoJSON 规范](https://geojson.org/)
## 许可证
MIT License - 开源友好
---
**预计开发时间**: 2-3 天(核心功能)
**扩展功能**: 根据需求迭代