a550d6d0fd
- Next.js + MapLibre GL 3D 地球 - CARTO dark-matter vector 底图 - 动态星空背景(闪烁、漂移、流星) - 经纬网格线(graticule) - 文物数据图层与朝代/类别筛选 - 中国风信息弹窗 - 中英双语 i18n - LayerPanel + MapControls 浮动面板
9.9 KiB
9.9 KiB
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: 项目初始化
# 创建 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:
{
"nav": {
"title": "中国文物全球分布",
"search": "搜索文物或博物馆"
},
"map": {
"layers": "图层",
"filters": "筛选器",
"reset": "重置"
},
"dynasty": {
"shang": "商周",
"qin": "秦汉",
"tang": "隋唐",
"song": "宋元",
"ming": "明清"
},
"category": {
"bronze": "青铜器",
"porcelain": "瓷器",
"painting": "书画",
"jade": "玉器"
}
}
messages/en.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:
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`./messages/${locale}.json`)).default
}));
middleware.ts:
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 创建语言切换器
// 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 定义数据结构(支持双语)
// 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
# 安装 tippecanoe(macOS)
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
/** @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
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;
开发流程
- 初始化项目 → 安装依赖
- 搭建地图 → 验证 MapLibre 正常渲染
- 加载数据 → 展示示例文物点位
- 实现图层 → 按朝代/类别分层
- 添加交互 → 点击、筛选、搜索
- 优化样式 → 深色主题、响应式
- 测试部署 → Vercel 一键部署
数据来源建议
- 公开数据集: UNESCO、大英博物馆、卢浮宫等开放数据
- 研究报告: 流失文物统计报告
- 众包贡献: 社区上传补充
参考资源
许可证
MIT License - 开源友好
预计开发时间: 2-3 天(核心功能) 扩展功能: 根据需求迭代