当前位置: 首页 > news >正文

Pythoner 的Flask项目实践-绘制点/线/面并分类型保存为shpfile功能(Mapboxgl底图)

文章目录

    • 1、前端 Mapbox GL + Mapbox GL Draw
      • 1.1、mapbox-gl-draw插件
      • 1.2、实现效果
    • 2、后端 Flask
    • 3、下载游览

本文也是基于上几节文章中的案例项目继续完善实现新功能:MapboxGL作为底图,添加绘图工具,通过绘图工具面板绘制点线面,最后下载保存为shpfile,以zip压缩文件形式供用户下载使用。

具体实现思路,在 Mapbox GL 里:

  1. 添加 多个绘图工具(点 / 线 / 面 / 删除)

  2. 页面上有 多个功能面板(比如左边是“绘图工具栏”,右边是“图层/属性面板”)

  3. 用 Mapbox GL + Mapbox GL Draw + 自定义 CSS 布局 来实现。

1、前端 Mapbox GL + Mapbox GL Draw

  • 使用 Mapbox 自带的绘图插件(mapbox-gl-draw)

  • 提供 点 / 线 / 面 三种绘制工具,带默认工具栏面板

  • 用户可以自由绘制

1.1、mapbox-gl-draw插件

Mapbox GL 本身的核心库 不带绘图功能,但是官方提供了一个插件 mapbox-gl-draw,这是它的“自带”绘图工具扩展。

它提供了 点、线、面绘制工具栏 + 属性面板(绘制完成后可编辑、删除)。

  1. 基本示例(Mapbox GL Draw)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mapbox GL Draw 示例</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.16.1/mapbox-gl.css" rel="stylesheet">
<link href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.5.0/mapbox-gl-draw.css" rel="stylesheet" />
<style>body { margin:0; padding:0; }#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body><div id="map"></div><script src="https://api.mapbox.com/mapbox-gl-js/v2.16.1/mapbox-gl.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.5.0/mapbox-gl-draw.js"></script><script>
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';var map = new mapboxgl.Map({container: 'map',style: 'mapbox://styles/mapbox/streets-v12',center: [54.5, 24.0],zoom: 5
});// ✅ 添加 Mapbox Draw 工具栏(自带面板)
var Draw = new MapboxDraw({displayControlsDefault: true,   // 显示默认的所有工具controls: {point: true,line_string: true,polygon: true,trash: true,combine_features: false,uncombine_features: false}
});
map.addControl(Draw, 'top-left');  // 工具栏显示在左上角// ✅ 监听绘图事件
map.on('draw.create', updateFeatures);
map.on('draw.update', updateFeatures);
map.on('draw.delete', updateFeatures);function updateFeatures(e) {var data = Draw.getAll();console.log("当前要素:", data);alert("当前绘制了 " + data.features.length + " 个要素");
}
</script></body>
</html>

1.2、实现效果

这是最小可运行的 内置绘图工具 + 工具栏面板:

在这里插入图片描述

在地图左上角自动出现一个 工具栏面板:

  • 点 ✦

  • 线 ▬

  • 面 ⬠

  • 删除 🗑️

用户可以交互式画点、线、面,并随时修改或删除。

事件监听:

  • draw.create → 用户新建要素时触发

  • draw.update → 用户修改要素时触发

  • draw.delete → 用户删除要素时触发

  • 可以用 Draw.getAll() 获取当前所有要素(GeoJSON 格式)

说明:

Mapbox GL 原生没有绘图 UI,需要加载 mapbox-gl-draw 插件,这个插件就算是“官方自带”的绘图工具和面板,它返回的数据是标准 GeoJSON,所以很容易存数据库或导出为 Shapefile

导航栏小图标使用地址:https://fontawesome.com/icons/icons?s=solid

前端完整代码(drawsaveshpfile.html):

{% extends "home.html" %}
{% block title %}地图绘制并保存{% endblock %}{% block content %}
<!-- Mapbox GL JS -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>绘制点/线/面 → 分类型保存为</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 100px; bottom: 0; width: 100%; }
</style>
</head>
<body>
<h3>绘制点/线/面 → 分类型保存为</h3>
<button id="saveShpBtn">保存为 Shapefile</button>
<div id="map" style="width: 100%; margin-top:10px;"></div><!-- Mapbox GL -->
<link href="https://api.mapbox.com/mapbox-gl-js/v2.16.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.16.1/mapbox-gl.js"></script><!-- Mapbox GL Draw -->
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.5.0/mapbox-gl-draw.css" type="text/css"/>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.5.0/mapbox-gl-draw.js"></script><script>
mapboxgl.accessToken = 'pk.eyJ1IjoidGlnZXJiZ3AyMDIwIiwiYSI6ImNsaGhpb3Q0ZTBvMWEzcW1xcXd4aTk5bzIifQ.4mA7mUrhK09N4vrrQfZA_Q';var map = new mapboxgl.Map({container: 'map',style: 'mapbox://styles/mapbox/streets-v12',center: [54.5, 24.0],zoom: 5
});// 添加绘图工具
var Draw = new MapboxDraw({displayControlsDefault: true,controls: {point: true,line_string: true,polygon: true,trash: true,combine_features: false,uncombine_features: false}
});
map.addControl(Draw,'top-left');// 保存按钮
document.getElementById("saveShpBtn").addEventListener("click", function(){var data = Draw.getAll();if(data.features.length === 0){alert("请先绘制点/线/面!");return;}fetch("/save_shp", {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(data)}).then(res => res.blob()).then(blob => {// 下载文件var url = window.URL.createObjectURL(blob);var a = document.createElement("a");a.href = url;a.download = "draw_shpfile.zip";  // shp 通常是多文件,后端打包为 zipa.click();window.URL.revokeObjectURL(url);}).catch(err => alert("保存失败: " + err));
});
</script>
</body>
</html>
{% endblock %}

2、后端 Flask

  • 获取前端传过来的 GeoJSON

  • 根据要素类型(Point / LineString / Polygon)分类

  • 各自保存为独立的 Shapefile(点.shp / 线.shp / 面.shp)

  • 打包成 zip 返回

@app.route("/drawsaveshp")
def drawsaveshp():return render_template("drawsaveshp.html")@app.route("/save_shp", methods=["POST"])
def save_shp():data = request.get_json()if not data or "features" not in data or len(data["features"]) == 0:return {"error": "没有有效的绘制结果"}, 400# 分类存储points, lines, polys = [], [], []for feat in data["features"]:geom_type = feat["geometry"]["type"]geom = shape(feat["geometry"])if geom_type == "Point":points.append({"geometry": geom})elif geom_type == "LineString":lines.append({"geometry": geom})elif geom_type == "Polygon":polys.append({"geometry": geom})tmp_dir = tempfile.mkdtemp()# 保存点if points:gdf_points = gpd.GeoDataFrame(points, crs="EPSG:4326")gdf_points.to_file(os.path.join(tmp_dir, "points.shp"), driver="ESRI Shapefile")# 保存线if lines:gdf_lines = gpd.GeoDataFrame(lines, crs="EPSG:4326")gdf_lines.to_file(os.path.join(tmp_dir, "lines.shp"), driver="ESRI Shapefile")# 保存面if polys:gdf_polys = gpd.GeoDataFrame(polys, crs="EPSG:4326")gdf_polys.to_file(os.path.join(tmp_dir, "polygons.shp"), driver="ESRI Shapefile")# 打包成 zipzip_path = os.path.join(tmp_dir, "features.zip")with zipfile.ZipFile(zip_path, "w") as zf:for f in os.listdir(tmp_dir):if f.endswith((".shp", ".shx", ".dbf", ".prj")):zf.write(os.path.join(tmp_dir, f), arcname=f)return send_file(zip_path, as_attachment=True, download_name="features.zip")

3、下载游览

浏览器点击“保存”按钮 → 请求后端 → 自动下载 zip

在这里插入图片描述
效果:

在这里插入图片描述

在这里插入图片描述


“人的一生会经历很多痛苦,但回头想想,都是传奇”。


http://www.dtcms.com/a/403202.html

相关文章:

  • 汽车渗透测试自动化工具和过程
  • 南京大学 LLM开发基础(二)大语言模型解析 -- 基于HF LlaMA实现的讲解
  • 《企业级知识图谱从0到1的开发实录》
  • Java虚拟机——垃圾回收算法
  • 电商平台正在建设中网站页面营销策略英文
  • MCP协议:重构AI协作的未来,打破模型边界的技术革命!
  • 做网站要备案吗宁波seo公司排名榜
  • UE5 GAS 预测框架解析
  • SavingsPlan模型优化:AWS成本管理的性能飞跃
  • 从入门到精通【Redis】理解Redis持久化
  • 郑州做网站元辰提升学历的正规平台
  • 什么是无盘工作站?RARP用于无盘工作站等设备在启动时获取自己的 IP 地址。
  • Python在不同领域的应用案例
  • 《Muduo网络库:CMake构建集成编译环境》
  • IDEA services面板+自动运行项目
  • 云原生网关Higress介绍与部署指南
  • 手机网站是怎么做的图片设计制作软件
  • 亚像素边缘检测思想
  • 云服务器需要备案吗?如何备案
  • AutoDL使用
  • 检察院门户网站建设方案磁力库
  • 时序数据库选型指南:Apache IoTDB引领数字化转型新时代——核心概念与关键技术解析
  • Hash算法全解析:原理、安全风险与全球法规要求
  • odoo阿里云大模型多字段内容翻译
  • 【硬核对比】Hive与MySQL全方位深度对比:从架构、SQL语法到应用场景,搞懂选型不踩坑
  • 【Java并发】深入解析ConcurrentHashMap
  • 【Windows10】MySQL9.4安装配置
  • 网站建设怎么做账安徽鲁班建设集团网站
  • 芋道源码 - 连接消息队列 rabbitmq
  • 语义三角论对人工智能自然语言处理中深层语义分析的影响与启示