之前在做地图可视化项目时数据同学导出一份.geojson文件前端页面加载后地图上什么都没有浏览器控制台也没有任何报错。排查到最后发现coordinates数组里混入了好几个空数组导致部分要素的几何解析直接失败地图层却“静默”吞掉了这处异常。这类问题非常典型。GeoJSON 本身是纯数据格式工具链只负责解析不负责“判断数据对不对”。但数据一旦有问题渲染端往往表现得很隐晦。直到我看到一个名为 GeoLint 的开源项目思路一下就清晰了它把 ESLint 的设计理念搬到了 GeoJSON 数据校验上让静态检查、规则配置、问题报告标准化。这篇文章就来系统拆解 GeoLint 到底是什么、它的规则体系如何设计以及如何把它接入实际项目。无论你是前端开发者、GIS 工程师还是日常处理地理数据的后端同学这篇文章都能帮上忙。读完你不仅能跑通 GeoLint还能理解规则背后“为什么要这么设计”遇到复杂数据质量问题也更容易定位根因。1. GeoLint 是什么从 ESLint 到 GeoJSON 校验1.1 一个典型的 GeoJSON 数据事故很多人第一次接触 GeoJSON 是因为前端地图组件。比如使用 Leaflet、Mapbox GL JS 或 ECharts 的地图功能通常需要加载一个 geojson 文件来定义区域轮廓或点标记。文件加载进来了地图却可能白屏、少一块区域或者弹出一堆莫名其妙的警告。从经验来看这类问题大多不是地图库的 bug而是数据本身“半残废”type字段拼写错误例如把Point写成PonitFeature对象缺少geometry或properties必填字段二维坐标与三维坐标混用[116.39, 39.9]和[116.39, 39.9, 30]交替出现多边形坐标环未闭合首尾点不一致经纬度越界比如经度写成 139.9实际上已经超出 180。这些错误不会导致 JSON 解析失败因为它们在语法上是合法的 JSON所以很难在加载阶段被发现。只有当地图渲染到一半或者某些空间计算突然异常时问题才露出马脚。1.2 引入 linter 的思路JavaScript 生态里有个成熟方案ESLint。它不是用来检查“JavaScript 能不能运行”而是检查“代码是否遵循团队约定的规范”。既然代码可以有 linter那数据为什么不行GeoLint 正是带着这种思路诞生的。它面向 GeoJSON 文件不是简单验证“是不是合法 JSON”而是提供了一套类似 ESLint 的机制内置一批可开关的规则通过配置文件控制规则的开启与参数统一输出“文件、行列号、错误级别、错误信息”的报告支持自定义规则方便团队沉淀自己的数据规范。类比一下如果JSON.parse是“语法检查”那么 GeoLint 是“代码规范检查”。前者保证数据能被解析后者保证数据符合业务要求、坐标系正确、结构完整、命名统一。1.3 GeoLint、ESLint、linter 三者关系很多同学对linter这个词不熟这里做一个简单区分概念说明linter泛指静态检查工具可以检查代码、配置、数据文件ESLint专注于 JavaScript/TypeScript 的 linter规则丰富、生态完善GeoLint借鉴 ESLint 设计理念专门面向 GeoJSON 数据的 linterGeoLint 不是要替代 ESLint而是把 ESLint 那一套成熟的“规则-配置-报告”模型迁移到地理信息数据领域。它解决的核心问题是在数据进入渲染层或计算引擎之前把结构问题和业务约束问题提前暴露出来。2. GeoJSON 格式基础与质量风险2.1 GeoJSON 的核心结构GeoJSON 是基于 JSON 的地理数据编码格式核心是GeoJSON Object和Geometry Object。常见的几种对象类型单个几何对象Point、LineString、Polygon多几何对象MultiPoint、MultiLineString、MultiPolygon要素对象Feature由geometry和properties组成要素集合FeatureCollection是实际项目最常用的外层容器。下面是一个标准Feature示例{ type: Feature, geometry: { type: Point, coordinates: [116.39, 39.9] }, properties: { name: 示例点, id: 1001 } }FeatureCollection则是把多个要素包起来{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: LineString, coordinates: [ [116.39, 39.9], [121.47, 31.23] ] }, properties: { name: 北京-上海 } } ] }结构本身不复杂但正因为结构灵活不同来源、不同工具生成的数据才会千差万别也容易混入各种“看起来没问题”的脏数据。2.2 高频数据错误清单结合实际项目经验我整理了一份高频问题清单。这些场景在 geojson 数据格式的日常处理中非常常见错误类型典型表现后果类型拼写错误type: Ponit解析器无法识别几何类型必需字段缺失Feature 缺少properties有些工具直接报错坐标数组为空coordinates: []几何无效渲染空白坐标维度混用二维、三维坐标共存空间计算异常经纬度越界经度 180 或纬度 90点位漂移显示错误位置多边形环未闭合首尾坐标点不一致面积计算错误、渲染变形要素没有属性properties: {}无法关联业务数据其中坐标维度混用是大坑。一个LineString的前两个点是[x, y]第三个点却是[x, y, z]很多解析库默认按三维处理结果后端空间索引直接混乱。2.3 为什么人工审查不可靠你可能会说这些错误“肉眼都能看出来”。但实际项目里的 geojson 文件动辄成千上万行人工检查根本不现实。前端拿到的 geojson 文件通常来自第三方数据平台或 GIS 工具导出经过多次转换后很难保证每一步都符合规范。此外很多需求是“结构合法但业务不合法”。比如一个区域轮廓数据要求必须有adcode和name字段但某个文件漏掉了adcode。这种问题不是通用工具能发现的必须依赖可配置的规则引擎。这正是 GeoLint 这类 linter 最有价值的地方。3. 像 ESLint 一样设计 GeoJSON 的 linter3.1 ESLint 的核心设计理念要理解 GeoLint 的架构先看 ESLint 做了哪三件事解析源代码为 AST拿到可遍历的语法树遍历 AST 并执行规则每条规则关注特定的节点类型汇总报告统一格式输出问题列表。这里最值得借鉴的是“规则只关注自己关心的节点”。一条规则不需要理解整个文件只需要在碰到某个节点时判断是否违规。这种设计让规则可以独立开发、独立测试、独立配置。GeoLint 面对 GeoJSON 时也采取了类似思路把 GeoJSON 文件解析成一棵对象树规则可以选择监听Feature、Geometry、Point、FeatureCollection等节点类型。碰到一个要素检查属性是否完整碰到一个坐标数组检查范围是否合法。各司其职互不干扰。3.2 GeoLint 的规则模型一个典型的 GeoLint 规则可以拆成这样module.exports { meta: { description: 检查坐标数组不能为空 }, create(context) { return { Point(node) { if (!node.coordinates || node.coordinates.length 0) { context.report({ node, message: Point 的 coordinates 不能为空 }); } } }; } };这里出现了规则的两个关键部分meta规则的元信息包括描述、是否可修复、文档地址create返回一个监听器对象定义该规则关心哪些节点。当一个 GeoJSON 对象被解析后GeoLint 会遍历整棵对象树。遇到Point节点就调用上面对应的Point(node)函数遇到FeatureCollection节点也会调用对应的监听函数。这种设计最大的好处是团队可以根据自己的业务追加规则。比如有的项目要求所有Feature都必须包含properties.name那就可以写一条required-properties规则而不是去改通用校验库。3.3 配置文件的组织方式ESLint 使用.eslintrc来管理规则GeoLint 也采用了类似的配置形态。下面是一份示例.geolintrc.json{ rules: { geolint/no-empty-coordinates: error, geolint/geometry-type: [ error, { allowed: [Point, LineString, Polygon] } ], geolint/required-properties: [ error, { required: [name, adcode] } ] } }规则值为off、warn、error三档对应 ESLint 的习惯off表示关闭warn只警告不影响命令退出码error会作为错误输出CI 中可以让流水线失败。带参数时写成数组形式第二项是规则参数对象。比如geometry-type规则允许你指定该文件里允许出现的几何类型超出范围就报错。4. GeoLint 实战从安装到跑通第一个规则4.1 环境准备GeoLint 通常是基于 Node.js 的命令行工具所以本地环境需要准备Node.js 16 及以上版本npm、yarn 或 pnpm 任意一种包管理器。安装方式很简单一般是通过 npm 全局或项目内安装npm install -g geolint如果是项目内安装更推荐通过npx直接执行避免污染全局环境npx geolint --init--init命令会生成一份默认的.geolintrc.json配置文件方便从零开始。这里需要说明不同项目的 CLI 参数可能略有差异具体以 GeoLint 项目 README 为准。本文以“ESLint 风格”的命令设计为例重点展示使用思路。4.2 准备一份待校验的 GeoJSON 文件先创建一份带有典型问题的数据文件路径为data/sample.geojson{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: Point, coordinates: [] }, properties: { name: 空坐标点 } }, { type: Feature, geometry: { type: Ponit, coordinates: [116.39, 39.9] }, properties: { name: 拼写错误点 } }, { type: Feature, properties: { name: 缺少 geometry } } ] }这份文件里有三个问题第一个点的coordinates是空数组第二个点的type拼写成了Ponit第三个点缺少了geometry字段。这三个问题都是 GeoJSON 数据中非常典型的“静默故障”不会导致 JSON 解析失败但会影响地图渲染和空间计算。4.3 编写配置文件在项目根目录创建.geolintrc.json{ rules: { geolint/no-empty-coordinates: error, geolint/valid-geometry-type: error, geolint/feature-required-fields: error } }三条规则分别对应刚才的三个问题no-empty-coordinates检查coordinates数组是否为空valid-geometry-type检查geometry.type是否为规范允许的类型feature-required-fields检查Feature是否包含geometry和properties。4.4 命令行执行校验在项目根目录执行npx geolint data/**/*.geojson预期输出会类似于data/sample.geojson 4:12 error coordinates must not be empty geolint/no-empty-coordinates 9:12 error invalid geometry type Ponit geolint/valid-geometry-type 16:6 error Feature must have geometry field geolint/feature-required-fields ✖ 3 problems (3 errors, 0 warnings)这种报告格式和 ESLint 非常接近先显示文件名再显示行列号和错误级别最后是规则名。开发者在终端里扫一眼就能知道数据哪里出了问题而不需要自己打开 JSON 一行一行核对。4.5 自动修复与手动修复部分规则支持自动修复比如坐标精度格式化、统一Feature字段顺序等。执行npx geolint data/**/*.geojson --fix--fix会自动处理可修复的问题。但要注意像“缺少 geometry”这种问题无法自动修复因为工具不知道你原本想表达什么几何类型。这类问题必须回到数据生产端去补齐。修复后的文件建议重新执行一次校验确认问题清零npx geolint data/**/*.geojson5. 编写自定义规则一个完整的例子5.1 什么时候需要自定义规则内置规则往往只解决通用问题比如结构是否合法、坐标是否为空、类型是否拼写正确。但业务项目里的很多约束是“独有”的。举几个例子所有要素必须有adcode字段且必须是六位数字点要素不能落在某些区域之外线要素的坐标数量不能少于 2 个多边形必须闭合属性字段命名必须统一为驼峰式。这些都能用自定义规则实现。5.2 规则接口设计在 GeoLint 的设计中自定义规则通常导出一个对象包含meta和create。下面这条规则用来检查 Point 坐标是否越界// 文件路径rules/no-invalid-coordinate-range.js module.exports { meta: { description: 经纬度坐标必须在合法范围内, docs: { url: https://example.com/rules/no-invalid-coordinate-range.md } }, create(context) { return { Point(node) { if (!node.coordinates || node.coordinates.length 2) { return; } const [lng, lat] node.coordinates; if (lng -180 || lng 180 || lat -90 || lat 90) { context.report({ node, message: 坐标越界: [${lng}, ${lat}] }); } } }; } };这里的Point(node)表示遍历 GeoJSON 时每当遇到一个type为Point的对象就执行这个回调。5.3 检查要素属性完整性再来看一个更贴近业务的自定义规则要求所有Feature对象必须包含properties.name和properties.adcode。// 文件路径rules/required-business-properties.js module.exports { meta: { description: 要素必须包含业务属性 name 和 adcode }, create(context) { return { Feature(node) { const properties node.properties || {}; const requiredFields [name, adcode]; requiredFields.forEach((field) { if (properties[field] undefined) { context.report({ node, message: Feature 缺少属性字段: ${field} }); } }); } }; } };这种规则解决的是一个很现实的问题数据文件下载下来后结构是合法的但业务字段缺失导致后面做数据关联时一堆 null。提前在数据入库前拦截成本最低。5.4 把自定义规则加载进配置自定义规则写好后需要在配置文件中注册。假设规则文件放在项目rules/目录下package.json中声明了geoJSON字段来指定规则目录配置可以这样写{ rules: { geolint/no-invalid-coordinate-range: error, geolint/required-business-properties: error } }GeoLint 会自动扫描本地或全局规则目录把no-invalid-coordinate-range这样的规则名映射到rules/no-invalid-coordinate-range.js文件。属于团队的规范代码就可以沉淀下来。新成员加入时只需要安装同一套配置文件就能在本地获得一致的数据检查结果。6. 在项目工程中集成 GeoLint6.1 在 Node.js 数据脚本中调用除了命令行GeoLint 也可能作为 Node.js 模块被其他代码调用。例如在数据处理脚本里先校验再入库// 文件路径scripts/validate-data.js const geolint require(geolint); const geojsonData require(../data/china.geojson); const report geolint.lint(geojsonData, { rules: { geolint/no-empty-coordinates: error, geolint/required-business-properties: error } }); if (report.errorCount 0) { console.error(数据校验未通过终止入库); console.error(report.output); process.exit(1); } console.log(数据校验通过);这种方式适合把 GeoLint 嵌入到“文件导入-清洗-入库”的完整流程中。数据进入业务系统前先过一道 linter不合格就直接拒绝写入。6.2 接入 CI/CD 流水线地理数据经常是团队协作产出为了保证主分支上的 geojson 文件始终可用可以在 CI 里加一道检查。以 GitHub Actions 为例增加一个校验任务name: geojson-lint on: push: paths: - data/**/*.geojson jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 - run: npm install -g geolint - run: geolint data/**/*.geojson这里利用了paths过滤只有当data目录下的 geojson 文件变更时才触发校验。任何一位同事提交了带错误的数据文件CI 就能立刻给出反馈不会等到前端页面白屏才发现。6.3 与 geojsonhint、JSON Schema 的配合在 GeoJSON 校验这个领域GeoLint 并不是唯一工具。已经有一些成熟方案比如 Mapbox 的geojsonhint和通用 JSON Schema 校验器。它们和 GeoLint 并不冲突而是互补geojsonhint更侧重于“是否符合 GeoJSON 规范”偏向语法结构层面JSON Schema 可以校验字段类型、必填字段但难以表达“坐标范围”“多边形闭合”这类空间规则GeoLint 更接近 ESLint 的定位支持规则开关和自定义业务校验。建议的工程分层是先JSON.parse保证可读再用 geojsonhint 或 JSON Schema 做结构校验最后用 GeoLint 做业务规则校验。这样三层校验各有侧重既不会重复也不会留下死角。7. 常见问题与排查清单7.1 高频问题表格用 GeoLint 校验 geojson 文件时遇到的报错大多可以归为下面几类问题现象常见原因解决思路规则不生效配置文件没有放在项目根目录检查.geolintrc.json路径和文件名自定义规则无法加载规则文件没有导出对象或目录配置错误确认module.exports语法和规则目录coordinates 报错但 JSON 合法坐标为空、维度不足、类型不是数字回到数据生产端修复原始数据多边形边界异常环未闭合或内环外环方向错误使用空间库做闭合检查和拓扑修复报告定位不准数据是压缩成一行的大文件使用编辑器或处理脚本格式化 geojsonCI 中使用报错全局安装的 geolint 在 CI 环境不可用改为 npm 依赖安装并配置 scripts7.2 从报错信息快速定位数据问题当 GeoLint 输出data/sample.geojson报告时行号和列号直接告诉你问题在哪个位置。这里建议遵循一个排查步骤先看规则名确认是哪一类问题再打开对应文件的行号找到具体对象在完整对象上下文里观察而不仅仅是看一个坐标数组能自动修复的先跑--fix不能自动修复的回到生成 GeoJSON 的源头修改。如果一份 geojson 文件报错几十个不要一个个手工改优先找出数据生成脚本的 bug。数据源头的问题解决了导出文件自然就干净了。7.3 如何打开和预览 geojson 文件前端开发中拿到 geojson 文件后除了用编辑器直接看 JSON也可以借助一些可视化工具快速观察数据是否符合预期QGIS最常用的开源 GIS 工具可以直接加载 GeoJSON查看要素和属性表geojson.io在线站点拖入文件即可地图预览VS Code安装支持 GeoJSON 预览的扩展能在编辑器里直接渲染Leaflet / Mapbox 的简单页面临时写个 HTML 页面加载文件。不过要注意这些工具适合“看大概”不适合做严格的数据质量检查。批量、自动化、可配置的检查仍然要交给 GeoLint 这类 linter 去做。8. 最佳实践与工程建议8.1 校验策略尽早、自动、可解释数据质量问题的修复成本会随着链路向后传递成倍增长。文件在生产端错了改原始数据最便宜等入库之后再改往往涉及清洗任务重跑等前端上线后再发现影响面就大了。所以 GeoLint 的接入点应该尽量靠前。数据导出、文件上传、提交代码这三个环节各加一道校验就能拦截绝大多数问题。8.2 规则推荐基线并不是规则开得越多越好。规则太多会让团队陷入无休止的改数据反而影响效率。这里推荐一个起步基线规则方向推荐等级合法 JSON 可解析必须type 字段正确必须 errorgeometry 和 properties 存在必须 errorcoordinates 非空必须 error坐标范围合法建议 error多边形闭合建议 error业务属性完整业务自定义先跑通前几条再逐步增加业务规则。每一次新增规则意味着团队对数据质量多一份共识不要一上来就全量开启。8.3 把规则文档化并纳入代码评审linter 的价值不止于“拦截错误”更在于“沉淀共识”。建议每个自定义规则都写好文档说明为什么要设这条规则什么情况下会误报遇到合法但特殊的数据该怎么豁免。在代码评审时如果一条新规则导致大量现有数据文件报警不要硬去改数据先讨论规则本身是否合理。linter 是工具不是目的数据服务于业务不应该反过来被规则绑架。8.4 注意性能和边界场景如果你在数据流水线中处理上百 MB 的 geojson 文件建议在 GeoLint 之前先做格式精简和压缩。linter 本身是静态分析不应该承担数据清洗的重量。另外要注意边界场景美国某地的经度范围、跨越 180 度经线的多边形、南极地区的坐标都容易触犯简单的范围规则。规则实现时最好加上“允许特定数据源跳过检查”的机制避免误伤合法数据。9. 小结GeoLint 的定位很有意思它把前端工程化里已经成熟的 linter 理念移植到了地理数据领域。结构合法不代表数据可用数据可用不代表符合业务规范。通过一套可配置、可扩展、可集成的规则体系我们完全可以把 geojson 文件的质量检查自动化。这篇文章从 GeoJSON 的常见数据问题出发介绍了 GeoLint 的规则模型、配置方式、CLI 使用、自定义规则编写以及 CI/CD 集成方案。核心要记住的几点GeoLint 解决的不是“JSON 能不能解析”而是“数据质量和业务约束是否达标”规则可以内置也可以团队自定义关键是沉淀自己的数据规范接入点越早越好校验结果要可读、可解释实际项目中建议把 GeoLint 与 geojsonhint、JSON Schema 组合使用各管一层。下一步可以尝试在自己的地图项目里引入一份配置文件拿真实的 geojson 数据跑一遍校验看看能揪出多少你之前没注意到的问题。数据质量问题越早暴露后续的地图渲染、空间计算和数据可视化就越省心。