Skip to content

About

🎯 ScenicAtlas--河南省景区图集,采用前后端分离架构,支持地图交互与景区数据展示,可通过替换地图数据和景区配置快速扩展至其他省份或地区。 — A web-based tourism visualization framework featuring interactive, zoomable maps and drill-down exploration of 18 cities and 864 scenic spots in Henan Province.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

ScenicAtlas — 河南旅游景区图集

ScenicAtlas logo

基于 Leaflet + DataV 的省域文旅可视化大屏:卫星影像 · 矢量省界 · 数据分级填色

Python Flask Vue Leaflet Vite DataV-Vue3 License

功能特性 • 快速开始 • 实现原理 • 数据与标准 • API 文档

项目简介

ScenicAtlas(景区图集)是一个前后端分离的文旅可视化项目,把河南省 18 个地级市、864 个景区的数据呈现在一张可缩放、可下钻的交互地图上。

后端用 Flask 读取本地资源、做聚合/搜索/详情,并对外暴露 JSON API;前端用 Vue 3 + Leaflet 渲染地图、DataV-Vue3 提供大屏组件:景区与城市按真实经纬度定位,地图既可叠加卫星影像底图,也可切换成**矢量省界 + 按景区数分级填色(choropleth)**的数据视图;点击城市会缩放到该市边界并展开其景区点,右侧详情面板联动展示当前景区(5A 景区附实景大图,可点开查看灯箱大图)。

数据本身来自一条独立的离线管线(Data_processing/):Excel 原始表 → DeepSeek 富化(简介/标签/推荐理由/推荐分)→ 百度地理编码(经纬度)→ scenic.json。运行时后端不再依赖该管线,只读取产出的资源文件。

功能特性

地图与交互

  • 双底图模式:地图模式(卫星影像 WebP 铺底)/ 普通模式(矢量省界 + 按景区数分级填色 choropleth,悬停区域高亮并浮出数据气泡),右上角一键切换
  • 真实地理定位:所有点位走经纬度,滚轮缩放、拖拽漫游;点击城市 fitBounds 到该市轮廓,铺满视区
  • 默认去噪:默认只显示 18 个城市气泡;点击某城市才展开该市景区点,避免 864 点糊屏
  • 省会标识:郑州以金色 ★ 标注;默认镜头定位省会郑州

联动与详情

  • 城市 ↔ 详情联动:点击城市 → 右侧详情切换到该市最高分景区(hero);切城市时镜头、详情、同城列表三者一致
  • 选中态:当前选中景区珊瑚色高亮(地图/列表统一);选中城市为珊瑚环,缩放进城后隐藏只留城市名。普通模式下已选区域珊瑚色、悬停区域青色,两态颜色区分清晰
  • 同城高分景点:该市景区按分数倒序全量展示(列表内滚动),当前景区高亮,选择景区时自动滚动跟随;切换城市时面板回到顶部

搜索与筛选

  • 关键词搜索(景区 / 城市 / 标签),输入即检索并自动聚焦命中目标
  • 等级筛选(5A / 4A / 3A / 2A),命中数为真实总数(后端不截断);筛选时隐藏 0 命中城市,结果中景区组优先于城市组展示;超长结果列表虚拟滚动
  • 等级配色一一对应(城市 teal / 5A 金 / 4A 橙 / 3A 蓝 / 2A 灰 / 选中珊瑚),图例与地图一致

5A 景区实景图

  • 22 张 5A 景区图片:详情面板顶部大图 + 搜索结果/同城列表缩略图(仅有图的记录显示)
  • 点击任意图查看灯箱大图(× / ESC / 点背景关闭)
  • 统一规格(详见图片处理标准)

大屏视觉

  • DataV-Vue3 组件:三栏统一科技边框(dv-border-box,覆盖层不挡交互)、指标数字翻牌(dv-digital-flop)、标题装饰线(dv-decoration)
  • 立体感(纯 CSS 滤镜):省界投影阴影、选中城市高光描边、卫星影像对比/饱和增强凸显地形起伏
  • 地图氛围层(MapAmbience + effects/):雷达扫光、同心脉冲环、辉光、科技网格、漂浮粒子;组件化可独立开关,铺在地图后方只填充空白区,普通模式自动减弱

体验与可访问性

  • 响应式:宽屏三栏(侧栏 / 地图 / 详情,100dvh 满屏),窄屏自动堆叠
  • 指标数字翻牌、详情切换淡入;选中景区/城市时列表自动滚动跟随,切城市时详情回到顶部
  • prefers-reduced-motion 降级动效、:focus-visible 焦点环、提升的文字对比度

实现原理

核心架构

浏览器 (Vue 3 SPA + Leaflet)
   │  HTTP:fetch /api/*  ·  /resources/*(图片 / GeoJSON)
   ▼
Flask (backend/app.py)
   ├─ api/routes.py              REST:bootstrap / search / cities / scenic / summary / top
   ├─ services/scenic_service.py 聚合 · 搜索 · 城市/景区详情 · hero 排序 · 城市中心点
   └─ storage/scenic_repository.py 读取 resources/ · 扫描 5A 图片索引 · 由 GeoJSON 计算经纬度范围
   ▼
resources/   scenic.json(864) · 省界 GeoJSON · 卫星 WebP · 5A 景区 WebP

(离线)Data_processing/  Excel → DeepSeek 富化 → 百度地理编码 → scenic.json

地图渲染要点(frontend/src/components/TourismMap.vue)

  • 底图:地图模式用 L.imageOverlay(卫星WebP, 地理边界) 铺底;普通模式隐藏影像、用 L.geoJSON 画矢量省界,并按各市景区数做分级填色(choropleth),区域可悬停高亮 + 数据气泡(已选珊瑚 / 悬停青色)。两种模式都基于同一份 GeoJSON 与真实经纬度,点位始终对齐。
  • 省界范围自适应:后端从 GeoJSON 顶点算出 minLng/maxLng/minLat/maxLat,前端据此 fitBounds,不写死。
  • 选中城市环的显隐:用 map.getBoundsZoom(省界) 算出"概览缩放",当前缩放超过它 0.6 级即判定为"已进城",隐藏珊瑚环只留城市名标签——自适应任意视口,不依赖固定 zoom 阈值。
  • 漫游流畅度:去掉了悬浮控件的 backdrop-filter(地图在其后移动会逐帧重算模糊);拖拽期间临时隐藏城市名标签层(避免 18 个常驻 DOM 每帧重定位)。
  • 大量点位渲染:景区点用共享 L.canvas() 渲染(而非每点一个 SVG 节点),避免进城瞬间上百点同时创建造成的卡顿;城市点/选中态仍用 SVG 以支持标签与 CSS 动画。镜头移动时临时隐藏选中珊瑚环,避免定半径标记随缩放动画"放大再弹回"的突变。

选取与排序逻辑(backend/services/scenic_service.py)

  • 城市 hero / 默认选中:每城景区按 recommendScore → 等级 → 坐标置信度 → 名称 倒序取第一(评分优先,坐标置信度仅作兜底)。
  • 城市中心点:优先用内置城市锚点;否则对该市景区坐标做 IQR 去离群后取中位数,避免被离群坐标带偏。
  • 5A 图片注入:scenic_image_index() 扫描 5Apic/*.webp,按 id 给记录注入 image 字段。

项目框架

ScenicAtlas/
├── start_dev.py                  # 跨平台一键启动器(后端 5001 + 前端 5173)
├── b_requirements.txt            # Data_processing 数据管线的 Python 依赖
├── backend/                      # Flask 后端
│   ├── app.py                    #   应用入口;注册蓝图、托管 /resources/<path>
│   ├── api/routes.py             #   HTTP 路由(薄适配层)
│   ├── services/scenic_service.py#   聚合/搜索/详情/排序等业务逻辑 + lru_cache
│   ├── storage/scenic_repository.py # 读取 resources/、5A 图片索引、算地理范围
│   └── schemas/scenic.py         #   TypedDict 数据结构
├── frontend/                     # Vue 3 + Vite 前端
│   ├── index.html · vite.config.js · tsconfig.json(开发期 /api、/resources 代理到 :5001)
│   ├── public/favicon.svg        #   站点图标
│   └── src/
│       ├── App.vue               #   串联搜索/城市联动/镜头聚焦/灯箱
│       ├── components/           #   TourismMap / TourismSidebar / TourismDetail / TourismTooltip / TourismLightbox
│       │   ├── MapAmbience.vue    #     地图氛围层容器(组合 effects/ 各特效,可独立开关)
│       │   └── effects/          #     RadarSweep / PulseRings / TechGrid / ProvinceGlow / FloatingParticles / CornerHud
│       ├── composables/          #   useTourismDashboard(状态编排)· useVirtualList(长列表虚拟滚动)
│       ├── api/scenic.ts         #   类型化 API 层(与后端 schema 对齐)
│       ├── types/scenic.ts       #   前端域/接口 TypeScript 类型
│       ├── assets/logo.svg       #   品牌标识
│       └── styles.css            #   全局样式
├── resources/                    # 运行时资源(后端只读)
│   ├── scenic/scenic.json        #   景区数据(864 条)
│   └── mapresource/
│       ├── mapdata/Ali_Data_VGeoAtlas.geojson  # 全精度省界(DataV.GeoAtlas)
│       ├── mappic/410000_z10_web.webp          # 卫星影像底图(WebP)
│       └── 5Apic/<id>.webp                     # 5A 景区图片(22 张)
└── Data_processing/              # 离线数据管线(独立维护,运行时不依赖)
    └── scenic_pipeline/          #   schema/api/io/prompts(含评分算法 scenic_schema.py)

技术架构

后端

  • Python 3.10+(开发于 3.12/3.13)
  • Flask 3.1(json.ensure_ascii=False 直出中文;lru_cache 缓存聚合结果)
  • Pillow(图片规格化脚本,把景区原图转为统一 WebP)
  • 标准库即可运行;数据管线另需 b_requirements.txt(openai / pandas / openpyxl / httpx 等)

前端

  • Vue 3 + Vite 6 SPA
  • Leaflet 1.9(地图渲染、imageOverlay、geoJSON、circleMarker;景区点用 canvas 渲染)
  • DataV-Vue3 1.7(大屏边框 / 数字翻牌 / 装饰线等组件)
  • TypeScript 渐进式打底(tsconfig.json + 共享类型 types/scenic.ts + 类型化 API api/scenic.ts,与现有 .js/.vue 共存;npm run typecheck)
  • 省界矢量来自阿里 DataV.GeoAtlas;组件化 + 单一 composable 管理状态

快速开始

环境要求

  • Python 3.10+
  • Node.js 18+(开发/构建前端)

安装

# 1) 后端运行依赖(仅需 Flask;图片处理脚本另需 Pillow)
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install flask pillow

# 2) 前端依赖
cd frontend
npm install
cd ..

b_requirements.txt 是 Data_processing/ 数据管线(DeepSeek 富化 + 地理编码)的依赖,仅在重新生成 scenic.json 时才需要安装,运行本应用不需要。

启动

# 方式 A:一键启动器(后端 :5001 + 前端 Vite :5173,自动开浏览器)
python start_dev.py start
#   python start_dev.py stop                 # 仅清理占用端口
#   python start_dev.py start --no-browser   # 不自动开浏览器
#   python start_dev.py start --local-only   # 仅绑定 127.0.0.1,不暴露局域网

打开 http://localhost:5173 即可使用。

后端 / 前端命令

后端(仓库根目录,已激活 venv):

python -m backend.app          # 以模块方式启动后端(确保 backend 包可导入),默认 :5001
python start_dev.py start      # 同时拉起 后端(:5001) + 前端 Vite(:5173)

直接 python backend/app.py 会因仓库根不在 sys.path 而报 ModuleNotFoundError: backend;请用 python -m backend.app 或 start_dev.py(后者已自动注入 PYTHONPATH)。

前端(frontend/ 目录):

npm install        # 安装依赖
npm run dev        # Vite 开发服务器(:5173,热更新,代理 /api 与 /resources 到 :5001)
npm run build      # 生产构建 → dist/
npm run preview    # 本地预览构建产物
npm run typecheck  # vue-tsc 类型检查(TS 打底,校验 types/ 与 api/)

数据与标准

数据流

  1. backend/storage/scenic_repository.py 读取 resources/ 中的数据,并从 GeoJSON 计算地图经纬度范围、扫描 5A 图片索引
  2. backend/services/scenic_service.py 负责聚合、搜索、城市/景区详情与排序
  3. backend/api/routes.py 暴露 API
  4. frontend/src/composables/useTourismDashboard.js 编排搜索、城市联动、选中与镜头聚焦
  5. frontend/src/components/TourismMap.vue 用 Leaflet 渲染地图与两种底图模式

图片处理标准

景区图片(当前仅 5A,22 张)统一规格后存放于 resources/mapresource/5Apic/,按景区 id 命名。

  • 格式:WebP(RGB;源图若带透明通道则展平)
  • 尺寸:等比缩放,最长边 ≤ 1000px(仅缩小、不放大)
  • 质量:quality=80、method=6
  • 命名:<4 位景区 id>.webp(如 0001.webp),与 scenic.json 的 id 对应
  • 接入:后端 scenic_image_index() 扫描 5Apic/*.webp,为对应 id 的记录注入 image 字段(/resources/mapresource/5Apic/<id>.webp);前端用于详情面板大图 + 搜索结果/同城列表缩略图(仅有图的记录显示)
  • 更新流程:把原图放任意暂存目录 → 用 Pillow 按上述规格转 WebP、覆盖到 5Apic/ 对应 id → 文件名不变,无需改后端(浏览器需硬刷新 Ctrl+F5 才能看到新图)

参考转换片段:

from pathlib import Path
from PIL import Image

MAX_EDGE = 1000  # 最长边像素上限


def normalize_scenic_image(src_path: str, scenic_id: str, out_dir: Path) -> None:
    """统一为 5A 详情用的 WebP(RGB、最长边 ≤ 1000、quality 80)。"""
    img = Image.open(src_path).convert("RGB")
    width, height = img.size
    if max(width, height) > MAX_EDGE:
        ratio = MAX_EDGE / max(width, height)
        size = (round(width * ratio), round(height * ratio))
        img = img.resize(size, Image.Resampling.LANCZOS)
    img.save(out_dir / f"{scenic_id}.webp", "WEBP", quality=80, method=6)

景点评分标准

recommendScore 为 0–100 的推荐分,在 Data_processing/scenic_pipeline/scenic_schema.py 的 calc_recommend_score(level, category, tags) 中计算:

  1. 基础分(按等级):5A=85 / 4A=75 / 3A=65 / 其它=55
  2. 类别加分:自然风光、历史文化、宗教文化 +5;红色旅游、博物馆展馆 +3;其它 +0
  3. 标签加分(累计上限 +10):重点标签每个 +2(历史文化 / 自然风光 / 山岳 / 古建筑 / 宗教文化 / 瀑布 / 世界遗产),其余约 32 个常见标签每个 +1
  4. 组合加分(各 +1):山岳+瀑布;亲子+研学;古镇+古建筑;宗教文化+(寺庙|道观);自然风光+(湖泊|峡谷|森林|湿地|瀑布)
  5. 标签丰富度:去重后标签数 ≥4 个 +1,≥5 个 +1
  6. 最终裁剪到 [0, 100]

应用侧排序:城市「默认选中 / 头牌景区(hero)」按 recommendScore → 等级 → 坐标置信度 → 名称 倒序取第一;「同城高分景点」为该市景区按分数倒序的全量列表(含当前景区,高亮显示)。

运行时资源

资源 说明
resources/scenic/scenic.json 景区数据源(864 条)
resources/mapresource/mapdata/Ali_Data_VGeoAtlas.geojson 全精度矢量省界(DataV.GeoAtlas);普通模式分级填色 + 区域高亮/气泡 + 算经纬度范围
resources/mapresource/mappic/410000_z10_web.webp 卫星影像底图(由原 97MB PNG 降采样为 ~0.8MB WebP)
resources/mapresource/5Apic/<id>.webp 5A 景区图片(22 张,按 id 命名)

API 文档

所有接口返回 JSON(Content-Type: application/json,中文不转义)。

方法 路径 说明
GET /api/health 健康检查
GET /api/bootstrap 首屏全量数据:stats / assets / bounds / records / cities / featured / top*
GET /api/summary 概览:stats + topCities + topScenic
GET /api/top?limit= 热门城市与景区
GET /api/search?q=&level=&minScore=&limit= 综合检索(不传 limit 返回全部命中)
GET /api/cities?level=&q= 城市汇总(含可见计数)
GET /api/cities/<id 或 名称> 城市详情(该市景区列表);前端统一传 id
GET /api/scenic?city=&level=&q=&minScore=&limit=&offset= 景区列表查询
GET /api/scenic/<id> 单景区详情 + 同城相关
GET /resources/<path> 静态资源(图片 / GeoJSON)

跨平台说明

  • Windows / macOS / Linux 均支持:start_dev.py 按操作系统分支处理端口清理(Windows PowerShell/netstat vs POSIX lsof)、进程组(CREATE_NEW_PROCESS_GROUP vs setsid)、venv 路径(Scripts vs bin),并为后端进程注入 PYTHONPATH。

故障排除

  • 后端报 ModuleNotFoundError: backend:用 python -m backend.app 或 python start_dev.py start 启动,不要直接 python backend/app.py。
  • 前端地图/图片 404 或空白:确认后端在 :5001 运行、Vite 代理生效;首屏依赖 /api/bootstrap。
  • 更新了 5A 图片但页面没变:URL 未变、内容变了属浏览器缓存,硬刷新(Ctrl+F5)。
  • 端口被占用:python start_dev.py stop 清理 5001 / 5173 后重启。

说明

  • 后端运行时不再依赖 Data_processing,只读取 resources/ 中的产物;数据管线可独立维护、按需重新生成 scenic.json。
  • 前端可独立 npm run build,后端可继续扩展查询接口与数据分层。

License

本项目基于 MIT License 开源,版权 © 2026 ScenicAtlas。

About

🎯 ScenicAtlas--河南省景区图集,采用前后端分离架构,支持地图交互与景区数据展示,可通过替换地图数据和景区配置快速扩展至其他省份或地区。 — A web-based tourism visualization framework featuring interactive, zoomable maps and drill-down exploration of 18 cities and 864 scenic spots in Henan Province.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages