从野外记录本到离线数字工具:geolog-app的PWA实践

📅 发布时间:2026/9/8 5:44:56
从野外记录本到离线数字工具:geolog-app的PWA实践
简介这是一份面向地质学或地球科学领域开发者与数据分析人员的 Python 应用资源基于项目名与结构推断它可能用于地质数据的处理、可视化或简单 Web 展示适合正在学习 Python 在地学场景落地的读者参考。压缩包共 22 个文件大小仅 14KB核心为 14 个 Python 脚本覆盖 Django 项目配置、视图、模型、迁移与测试等后端逻辑同时包含 requirements.txt、Procfile 等部署配置以及一个 HTML 模板、一个 CSS 样式文件和 SQLite 数据库文件基本呈现了一个小型 Web 应用的完整目录结构便于快速理解项目组织方式。目前已有 162 人浏览学习属于轻量级入门示例。借助源码中的模块划分和依赖声明读者可以了解 Django 基础工程搭建、数据库表结构定义、页面模板渲染及部署文件配置等关键知识点若对地质数据展示感兴趣还能基于这套框架扩展自己的可视化功能尤其适合 Python 初学者作为首个 Django 项目拆解参考。 2019年夏天我在大巴山南麓做野外踏勘。白天顶着太阳打产状、记描述、拍照片晚上回驻地第一件事是打开笔记本电脑把白天写在记录本上的地层岩性、构造产状、照片编号一项一项往Excel里誊。一份记录大概要花一个半到两个小时稍不留神错行后面画柱状图时要来回核对。那段时间我一直在想既然手里已经有一台智能手机、有GPS、有大容量存储为什么野外记录这件事还停在纸笔时代回到成都以后我决定用业余时间自己做一个真正顺手的野外记录工具这就是geolog-app的由来。这个项目最初的目标很简单做一个在野外没有信号的环境下也能正常用的地质数据采集应用能快速记录地质点信息、关联野外照片、画轨迹数据不用联网就能存下来回到室内一键导出成表格和GIS数据不用再人工二次整理。如果你也是地质专业的学生、从事工程勘察的同行或者对离线优先local-first类应用开发感兴趣的开发者这篇文章会把我的技术选型思路、数据模型设计、核心实现细节和踩过的坑都摊开给讲一遍。1. 项目背景与需求拆解从野外记录本到“一键数字化”1.1 传统野外地质调查工作流的痛点传统野外地质记录的工作流大概是这样的手拿地质锤和罗盘到一个观察点拿出记录本画信手剖面、记岩性描述、测产状再用数码相机拍一组照片照片编号和记录本里的点号要保持手动对应关系。回到室内之后要面对一堆比较痛苦的事一是野外记录是手写的字迹、涂改、潦草程度各异录入Excel时非常容易看错二是照片和文字描述脱节一张照片对应哪个地质点经常要翻相机的时间戳去核对三是后期想画地质图要把点位坐标用GPS手簿导出来再转成shapefile链路又多又长。要承认的是专业领域里已经有像“野外数据采集系统”这类商业软件但大多数面向大单位采购授权贵部署重而且交互设计往往还在用十几年前的桌面软件思维。对于个人科研、小团队踏勘、学生毕业设计这种场景一套能直接打开网页就能用、不需要配置服务器、数据自己保存的轻量方案反而更接地气。1.2 geolog-app 的定位与功能边界分析了这些痛点后我给自己定的产品边界很清晰不要做成一个大而全的“地质信息系统”只做“从野外采集到室内整理第一公里”这一件事。因此geolog-app第一版定位成浏览器端的渐进式Web应用PWA核心功能收敛为四个模块地质点快速记录经纬度、海拔自动获取岩性、时代、风化程度等字段用下拉选择加自定义补全描述信息做成结构化表单。照片关联拍照后自动读取GPS和拍摄时间与当前地质点绑定支持批量挂载。路线轨迹在离线地图上实时显示位置记录行动轨迹标记已采集的地质点。数据导出一键生成CSV表格、GeoJSON文件和带缩略图的HTML报告方便直接进QGIS或者OpenOffice等工具。这个边界确定以后后面所有技术选型都是围绕“轻量、离线、标准数据格式”这三个关键词来做的。2. 整体架构与技术选型为什么是 PWA IndexedDB Leaflet2.1 技术栈清单与选型理由先给出一份项目最初两星期内定下来的技术栈清单。这套组合在2020年前后已经非常成熟放到今天依然不落伍依赖很少维护轻松前端框架Vue 3全家桶。选它不是因为版本新而是单文件组件天然适合做表单密集型页面地质记录页的字段多、联动多用响应式的UI描述起来比jQuery时代轻松太多。构建工具Vite。开发热更新快打出来的包做静态托管很方便USB拷到内网电脑也能跑。地图组件Leaflet 1.7 OpenStreetMap离线瓦片。Leaflet的插件生态齐全marker、polyline、坐标拾取体积只有40KB左右比OpenLayers轻很多对手机浏览器够用。本地数据库IndexedDB操作层用Dexie.js封装。Safari、Chrome、微信内置浏览器都支持容量可以到几百MB甚至GB级比localStorage的5MB限制靠谱得多。定位浏览器的Geolocation API 原生GPS。在Android的Chrome里能调到高精度GPSiOS对后台定位限制严格所以我把定位设计成“点击获取”而不是持续监听。PWA壳manifest Service Worker用Workbox生成用于缓存静态资源和离线地图瓦片。为什么不用Electron做桌面客户端也不用Flutter做原生App原因很简单地质工作者手头的电脑系统五花八门Windows、macOS、Linux都有而且野外用的可能是学校机房的老机器装软件权限受限。一个PWA意味着只要这台机器有浏览器就能直接打开使用离线瓦片和数据库都存在浏览器里换机器的时候导出一次数据即可迁移。2.2 本地优先Local-first方案的优势项目名字里有app但它不需要走应用商店这是刻意为之。野外环境最关键的要求是网络是不可靠资源硬件是有限的资源。传统App要登录账号、要同步服务器这一点在深山老林里是一种负担。本地优先架构把所有业务数据写入浏览器内置的IndexedDB所有操作的服务端依赖降为零。这意味着打开应用不需要联网加载本地缓存的壳资源即可。写数据不经过网络层响应速度就是本机磁盘写入速度毫秒级。数据权完全在用户手里没有隐私泄露和上云合规的问题非常适合科研数据管理。断裂网络下依然可以完整工作回到有网环境后再手动把数据备份文件导到其他设备。作为补充我做了一个“手动同步”机制它不是服务器自动同步而是将数据打包成一个JSON文件可以存到私有网盘也可以U盘拷贝。地质数据本质上是小数据量、高价值的文本加照片一次野外采集最多几万个点JSON完全撑得住不需要后端数据库。2.3 数据模型设计让数据结构贴近地质调查规范很多个人项目死在“没有认真设计数据表结构”这个坎上后面加字段成了灾难。所以第一版写代码前我先花了足足一个晚上用草稿纸画数据关系最终确定了三个核心对象geopoint地质点、photo照片、track轨迹外加四个辅助字典表lithology岩性、strata地层单位、structure构造类型、weathering风化程度。geopoint表是核心设计上参考了区域地质调查中常用的记录项id自增主键或UUID我用了UUID方便离线合并。name点号例如D01、GPS-015用户在野外可以自定义前缀。latitude/longitudeWGS84坐标系十进制度数最多保留六位小数。elevation海拔单位米。浏览器定位接口带海拔但精度差别大所以允许离线后手动修改比如从地形图上读取。rockType岩性关键字从字典表中选择同时留freeText字段允许补充像“含角砾凝灰岩”这种细化描述。attitude产状对象包含走向、倾向、倾角前端做成三个数字输入框后端存成JSON字符串。description野外描述这是最自由的字段我做成多行文本但提供相似模板比如“灰—深灰色中厚层状灰岩发育方解石脉局部溶蚀现象明显”用户可以一键插入再修改省很多打字时间。photo对象和geopoint之间是多对一关系通过pointId关联。每张照片除了保存本地路径或Blob对象还自动提取拍摄时间和Exif中的经纬度。这里有一个容易踩的坑手机厂商对Exif的GPS写入策略差异很大有的手机默认不写坐标所以我在拍照模块里做了一个兜底——用拍照时Geolocation API拿到的实时位置覆盖Exif坐标保证每张照片都有空间位置信息。track对象按GeoJSON的LineString格式存储保存为坐标点数组和对应时间戳这样回放轨迹时可以做动态效果。3. 核心功能实现要点记录、关联、导出一条线3.1 地质点记录表单的交互设计地质记录这个场景有个和普通表单不一样的地方用户的手指可能刚碰过岩石粉末可能戴着薄手套也可能正在雨里打着伞。所以表单交互必须做到“一键直达”和“大按钮优先”。表单首屏默认只有点号、经纬度、海拔和岩性四个字段。点击“获取当前位置”按钮定位成功后会同时填充经纬度和海拔字段右上角会显示定位精度比如±5m。精度较差时我用橙色三角图标提示用户可以稍后重新定位。新增地质点时默认从字典表给出下拉选项比如岩性下拉列表的选项包括灰岩、白云岩、砂岩、页岩、玄武岩、花岗岩、片岩、片麻岩、千枚岩、板岩、大理岩、混合岩、砾岩、泥岩、凝灰岩、安山岩、流纹岩等每个字典项还可以维护别名比如“灰岩”的别名有“石灰岩”。描述字段引入“常用模板”是第二版才加的功能但实测下来效率提升最大。野外描述其实高度套路化比如沉积岩的套路是“颜色结构构造物质成分化石风化特征厚度接触关系”我为每类岩石准备了三到五套模板点击模板之后自动插入当前点描述只需要改数字和局部特征词记录时间从五分钟缩短到一分钟。3.2 照片与GPS坐标的自动关联照片管理是地质数据里最容易乱套的部分。geolog-app里我实现了两种照片采集方式。一种是在app内直接调用摄像头拍照使用HTML的captureenvironment属性拍照后照片以Blob形式存入IndexedDB同时触发定位API获取当前位置。第二种是从相册选择已有照片这个时候会尝试读取图片的Exif信息用exifr这个库解析GPS坐标和拍摄时间如果Exif里没有坐标就弹一个手动匹配地理位置的对话框允许用户选择当前地质点作为照片位置。照片存入时还有一个必须在代码里处理的细节是图片压缩。不压缩的话一张1200万像素的手机照片体积大概在2MB到4MB之间一天拍两百张就是600MBIndexedDB虽然能存但手机存储空间会告急而且导出的HTML报告会变成一个几百MB的文件。我引入了一个canvas压缩管线最大边压缩到2048像素JPEG质量设为0.72实测单张照片大约300KB肉眼在屏幕上观察野外细节完全够用。“原图无损”选项保留着但默认关闭适合需要看岩石显微结构的场景。3.3 离线地图、轨迹标注与数据导出虽然是“离线应用”但地图的底图始终需要素材。我的方案是在进入野外之前在Web端选择一个兴趣区域可以画多边形应用根据区域范围自动计算出需要的瓦片金字塔层级比如第8级到第17级然后批量下载OpenStreetMap瓦片并存入IndexedDB缓存。下载完成后这部分瓦片就永久保存在设备里野外即便完全断网也能正常平移动图。轨迹功能利用了浏览器的watchPosition方法每五秒记录一次坐标并将坐标点实时绘制到Leaflet的polyline上。采集地质点时应用自动生成一个带点号的圆形Marker点击Marker可以跳转到该点详情。这里我特意做了防误触设计Marker半径做成了地图像素上的12px比Leaflet默认大野外手指点起来不容易弹错同时用不同颜色区分“已记录描述”和“只打了点”两类标记。数据导出模块是我写的最早的模块因为我觉得一个数据采集工具要是导不出标准格式就毫无意义。目前支持三种导出格式CSV表格列出所有地质点包含坐标、海拔、岩性、产状、描述贴合通用电子表格。GeoJSON文件可以直接拖进QGIS作为矢量图层继续做花纹填充和出图。自带资料的HTML报告打包所有照片缩略图和野外描述按照点号排序适合直接微信发送给合作者快速浏览。实现导出时需要注意一个坑浏览器下载两个文件以上某些浏览器会询问多次存储权限体验不好。我的做法是将整个导出活动做成两步先导出原始数据CSVGeoJSON打包为ZIP再导出图文报告单独的HTML用JSZip来打包ZIP文件。4. 踩坑实录与排查技巧4.1 定位精度的坑坐标系偏移与冷启动这个项目早期在调试定位时经常出现“明明站在同一块露头上记录的点位却漂到坡下去了”的情况排查之后发现有两个诱因。第一个是坐标系问题。浏览器Geolocation API原生返回的是WGS84坐标而国内很多在线地图底图包括一些离线瓦片源使用的是加偏坐标系GCJ-02。如果你把WGS84坐标直接叠加到加偏底图上偏移量少则几十米多则两百米在1:50000地质图上看起来就像点错位碳酸盐岩界线上。我的做法是不用在线地图底图改用OpenStreetMap标准WGS84瓦片同时在应用里给了一个坐标转换小工具方便用户在底图之间切换时做转换。设计上我建议所有原生数据都以WGS84存储显示层再根据底图设置决定是否转换这样数据本身永远是干净的。至于“偏移”背后的更多细节属于地图合规范畴这里不展开只提醒一句野外记录务必以GPS原生坐标为基准不要基于加偏底图反推点位。第二个是冷启动问题。GPS模块在刚打开浏览器时如果长时间待在室内再走到室外定位结果往往直接取用基站或Wi-Fi网络定位数据误差可能达到500米以上。我的规避措施是在“获取当前位置”按钮上做了连续三次采样如果相邻两次定位结果相差超过30米就显示提示“位置不稳定请等待GPS锁定”并且十秒内自动再补采一次。实际在山里使用下来一般等待二三十秒精度能稳定在5米以内。4.2 存储限制与照片体积问题IndexedDB理论容量很大但不同浏览器的实际配额策略很不一样。我实测过Chrome在Android上能稳定用掉几个GBiOS Safari通常在500MB左右会提示站点存储空间不足而桌面端Firefox的配额策略相对保守。为了解决这个问题我在“设置”页面放了存储统计面板显示当前已用空间、照片张数和总容量上限并且提供“清理未关联照片”按钮一键删除没有被任何地质点引用的孤儿图片。这个功能看似简单野外用几天之后清理出几百MB是很常见的事情。再说照片方向问题用手机竖屏拍出来的照片很多时候在Web页面里显示会被旋转90度。这个坑源自Exif里的Orientation字段。我用canvas做压缩管线时把Orientation字段也一并解析并应用到画布导出时统一输出成标准方向的JPEG。如果这一步漏掉报告链接一打开一半照片是躺着的就是断送项目口碑的细节。4.3 常见问题排查速查表问题现象可能原因解决办法点击“获取当前位置”无反应未授予定位权限或浏览器阻止了非HTTPS页面的Geolocation确认通过localhost或HTTPS访问在浏览器设置里手动放行定位权限坐标为0,0定位失败偶发错误返回无效值判断isNaN或者值等于0时重新采样后台自动重试三次地图瓦片加载不出来选择的区域层级过大下载中途切后台分多次下载每次区域控制在一度范围内检查IndexedDB是否有缓存导出GeoJSON在QGIS打开无图层QGIS需要指定CRS在GeoJSON文件里显式写入EPSG:4326的CRS属性QGIS就能正确识别导入照片不显示浏览器读取本地文件到Blob时未持久化确认照片Blob已经存入IndexedDB不要在会话结束后仅引用临时object URL应用更新后旧数据不见了Service Worker缓存了旧版shell更新后强制刷新并清一次缓存把版本号放到manifest配置里管理4.4 一个值得留意的多设备合并经验因为我一开始就考虑到野外的数据最终要汇总到同一台电脑上所以每条数据都带UUID和updatedAt字段。回到室内以后有两台机器分别记录了不同路线就可以用“导出全部数据”生成两个JSON文件然后通过“导入备份”功能合并。合并策略并不复杂以updatedAt为准谁的最新就用谁的同时保留同一点号不同描述的历史版本。实测下来有一个小坑两天的数据库里点号都是从D001开始编号合并后会出现重复点号导致表格里看起来像有两个D001。我后来做了一个“自动重编号”选项导入时可以按合并顺序统一重排点号并把照片文件名里的点号一并替换。这个功能在项目交数据给负责人的时候帮了大忙不需要再用Excel函数处理一遍重复值。在我把这些功能真正带到下一轮野外项目里用掉一个完整季度之后最大的体会是工具类项目最核心的成本不在写第一版功能而在持续打磨那些“舒服的小细节”。一个自动补全的描述模板、一次点按就能完成的照片关联、一键导出的标准化数据这些看起来不那么“核心技术”的东西恰好是让一个工具从“能用”变成“好用”的分水岭。现在geolog-app已经是我个人野外踏勘的标准配置我也把项目开源出来如果你也有类似的地质记录或者其他领域的野外数据采集需求完全可以把它当作一个参考样板替换掉地质字典表和导出模板几分钟就能改成植物调查或者考古点记录工具。最后再分享一个小技巧app里我设计了一个“草稿”概念地质点在没有点“完成”之前不会出现在轨迹图上这样遇到临时拿不准、需要回头补充的描述也可以先存半截记录过程中真的很少丢数据了。本文还有配套的精品资源点击获取