GeoJSON全链路实战:格式选型、坐标转换、ArcGIS互转与排错
地理数据这行干久了你会发现一个挺有意思的现象同事之间传来传去的那份“地图数据”十有八九不是Shapefile也不是什么数据库导出的二进制包而是一个能直接用记事本打开、几十KB的文本文件——GeoJSON。我第一次认真对待这个格式是因为一个很具体的麻烦外业同事发来一堆带经纬度的表格我想在图上快速看一眼点位分布装一套桌面GIS软件太重用在线工具又担心数据被传到外面。后来索性把GeoJSON这套东西从头到尾捋了一遍包括结构原理、geojson.io的本地部署、ArcGIS的进出转换以及那些让人半夜抓头的排错现场。这篇就把我踩过的坑和验证过的做法整理出来适合刚接触空间数据的前端、做数据交付的测绘内业、以及需要把业务数据快速上图画面的产品同学。不管你是第一次听说GeoJSON还是已经用它传过几次数据但总在坐标系上翻车下面的内容应该都能对得上你的场景。1. 先把格式选对GeoJSON在数据链路里的位置1.1 一份JSON凭什么能当地图用很多人第一次看到GeoJSON时的反应是“这不就是个JSON吗”。没错它的本质就是JSON只是约定了一套描述空间对象的键名规则。一个点、一条线、一个面在GeoJSON里就是一组按顺序排列的坐标数组外加一个type字段告诉解析器“这串数字该怎么理解”。这种设计的妙处在于它把“几何形状”和“业务属性”彻底解耦了几何部分由coordinates负责业务部分全部塞进properties两边互不干扰。这种解耦带来的直接好处是前端拿到数据后不需要任何GIS库就能自己遍历。比如你想统计某个类型的要素有多少个直接对features数组做一次filter就完事完全不用管坐标在哪个投影下。而换成Shapefile你得先解析.shp的二进制头再读.dbf的属性表两个文件的记录顺序还得靠索引对齐光是写读取代码就够折腾半天。GeoJSON把这些全变成了数组下标代价是文件体积变大、精度表达不如二进制紧凑。我在实际项目里对它的定位很明确GeoJSON是数据交换格式不是数据存储格式。跨系统传数据、给前端喂图层、做小规模成果交付用它最省事但如果是几十万要素的底图数据老老实实上数据库或者切片别拿GeoJSON硬扛。1.2 和Shapefile、KML、CSV的横向对比选型这件事光讲GeoJSON好没有意义得放在具体对比里看。下面这张表是我自己整理的口径覆盖了日常最常打交道的四种格式对比维度GeoJSONShapefileKML带经纬度的CSV文件结构单文件纯文本至少三个文件.shp/.shx/.dbf单文件XML单文件表格可读性高记事本可看低需专用工具中标签冗长高几何类型点线面及集合一个文件可混合一个文件只能一种几何点线面均可仅点属性字段灵活可嵌套字段名限10字符类型受限支持较完整受表格列数限制坐标系声明规范要求WGS84附带.prj文件内置坐标定义无体积大文本冗余明显小中到大极小典型用途Web传输、接口返回传统GIS成果交付地图展示、标注点位清单这张表里最容易被忽略的是“几何类型”那一行。Shapefile一个文件只能装一种几何你要是既有面状地块又有点状设施就得拆成两个文件分别管理交付时还得写文档说明。GeoJSON的FeatureCollection没这个限制点线面可以混在同一个features数组里前端按geometry.type分支处理就行。这一点在做“一张图”类需求时特别省心。另一个值得说的是坐标系的显式程度。Shapefile靠.prj文件声明丢了.prj数据就成了“无坐标系的黑户”打开后位置全凭猜。CSV更彻底压根没有坐标系概念那些经纬度数字到底是WGS84还是某个地方坐标系全靠传数据的人口头交代。GeoJSON的规范明确要求使用WGS84虽然这个要求经常被违反但至少有个基准可以对齐。1.3 什么场景不该用GeoJSON有些坑是选型阶段埋下的后面怎么优化都救不回来。我总结了三类不适合用GeoJSON的情况。第一类是要素量大且需要频繁渲染的场景。浏览器解析几十MB的JSON本身就慢解析完还要构建几何对象、做空间索引几万要素还能忍几十万就明显卡顿了。这类需求应该走向量瓦片或者服务端渲染GeoJSON最多作为中间产物。第二类是需要精确拓扑关系的场景。GeoJSON不保证相邻面共享边两个面之间的公共边界在文件里是两份独立的坐标串哪怕数值上有微小差异也不会自动咬合。做土地利用、行政区划这类需要严格无缝的数据得靠拓扑工具处理格式本身帮不了你。第三类是属性字段特别多、记录特别长的场景。JSON的键名会随每条记录重复出现字段一多体积膨胀非常夸张。我见过一个把二十多个业务字段塞进properties的GeoJSON光是键名重复就占了三成体积。这种数据用数据库或者列式存储更合适真要用JSON传输也该考虑抽离公共字段。2. 结构原理拆解从顶层对象到一行坐标2.1 三个核心对象Geometry、Feature、FeatureCollectionGeoJSON的对象体系其实只有三层理解这三层读任何GeoJSON文件都不会迷路。最底层是Geometry负责描述形状。它的type取值有七种Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon、GeometryCollection。前六种对应“点、多点、线、多线、面、多面”最后一种是几何集合可以把不同类型的几何打包在一起。每种几何的核心都是coordinates区别只在于数组的嵌套层数点是一层[x, y]线是两层[[x,y],[x,y]]面是三层因为面要有外环和内环洞的区分。中间层是Feature它是“几何加属性”的组合体。一个Feature必须包含type、geometry、properties三个成员其中geometry可以是上面任何一种几何也可以是null表示这个要素只有属性没有位置比如一条待定位的记录。properties是个自由对象放什么由你决定但规范建议保持扁平。顶层是FeatureCollection就是一个装着Feature的数组外加type字段。绝大多数业务场景交付的都是这一层因为一个文件里通常不止一个要素。看一段最小的完整示例{ type: FeatureCollection, features: [ { type: Feature, properties: { name: 监测点A, level: 2 }, geometry: { type: Point, coordinates: [116.3, 39.9] } } ] }这段结构没什么玄机但要注意一个细节FeatureCollection里的features必须是数组哪怕只有一个要素也得用方括号包起来。我见过有人写成单个对象结果某些解析器直接报错另一些解析器则静默跳过排查起来很费时间。2.2 坐标顺序这个坑值得单独说GeoJSON规范里写得很清楚坐标顺序是经度在前、纬度在后。但这件事在实际工作中翻车的概率高得离谱原因是几乎所有的GIS桌面软件在界面上都习惯显示“纬度、经度”或者“X、Y”这样的顺序人在抄数据的时候很容易顺着界面顺序写。坐标顺序错了会是什么现象如果你的点原本在某个城市经纬度写反后纬度值一旦超过90解析器可能直接报错如果没超过90数据就会被当成另一个位置通常跑到赤道附近或者大洋上。最典型的症状是一批点全部集中在经度0度、纬度0度附近或者呈一条奇怪的斜线分布。看到这种图第一反应就该是“顺序反了”。判断方法很简单看一眼原始坐标的数值范围。经度的合法范围是-180到180纬度是-90到90。如果你看到某对坐标里第一个数只有三四十第二个数却有一百多那基本可以确定写反了。批量修正也不难写个脚本遍历features把每个坐标数组的前两位交换即可但要注意递归处理嵌套层级别只改了最外层。注意坐标顺序问题和坐标系问题是两回事别混为一谈。顺序反了是数组元素位置错坐标系错了是数值本身需要做数学变换。前者改顺序就行后者必须走投影转换硬调数值只会越调越偏。2.3 properties里该放什么、不该放什么properties的自由度很高但自由不等于随便。我在多次数据对接后形成了几个习惯。字段名尽量用英文或者拼音避免中文键名。虽然JSON本身支持UTF-8中文键但下游如果有老系统或者某些解析库中文键容易出问题。字段值里的中文没问题键名还是保守一点。避免深层嵌套。有些同学喜欢在properties里塞一个对象里面再塞数组觉得结构清晰。问题是很多GIS工具读取属性时只认一层键值嵌套对象会被序列化成字符串或者直接丢弃。真需要分组用下划线拼接键名更稳妥。控制字段数量。前面说过键名重复会撑大体积如果确实字段多考虑把不参与渲染的字段拆到单独的属性表里用id关联。日期类型要特别小心。ArcGIS导出GeoJSON时日期字段通常会被转成毫秒时间戳一个数字前端拿到后得自己转格式。如果直接当字符串展示用户会看到一串看不懂的大整数。这一点在跨系统对接时最容易出问题因为两边的字段类型定义往往对不上。2.4 已经被规范移除的crs成员早期版本的GeoJSON允许在顶层写一个crs成员用来声明数据用的是哪个坐标系。这个设计本意是好的但实践中造成了很多混乱有的解析器认有的不认有的认了也不做实际转换只是把声明原样带过去。结果就是数据上明明写着“这是某个投影坐标系”但因为坐标值本身没做变换显示出来照样是错的。现行的GeoJSON规范已经把这个成员移除了明确规定坐标必须是WGS84下的经纬度。这意味着你不能指望在文件里写一句crs声明就让别人正确显示唯一的办法是把坐标值真正转换到WGS84再写进去。这一点在对接ArcGIS成果时尤其重要。很多内业数据是在投影坐标系下生产的单位是米数值动辄六位七位。如果直接把这样的坐标塞进GeoJSON的coordinates数组文件本身不会报错但任何按规范解析的工具都会把它当成经纬度处理位置偏到不知道哪里去。正确做法是先做投影转换再导出。3. geojson.io在线能用本地部署更稳3.1 在线版的边界在哪里geojson.io这个工具我用了很多年它的定位很清晰一个开箱即用的GeoJSON查看与轻量编辑工具。打开网页把文件拖进去地图上立刻显示在图上点几下能画点线面右侧表格能直接改属性改完可以下载回本地。对于“快速看一眼数据长什么样”这个需求它几乎没有替代品。但它的边界也很明显。第一数据要经过网络传输涉及敏感或者内部数据时这一条就过不去。第二底图依赖外部瓦片服务网络不通的时候地图区域是空白的只剩控件在。第三它是纯前端渲染要素一多就卡几千个点还能转上万个面基本就没法操作了。第四它不做坐标系转换你喂进去什么坐标它就按WGS84画投影坐标进去必然是错位的。所以我的判断标准是非敏感数据、要素量不大、只需要看看和微调用在线版最省事数据不能出内网或者需要长期稳定使用那就本地部署一套。3.2 本地部署的完整步骤本地部署的核心就三步拿代码、装依赖、起服务。但每一步都有细节。第一步把仓库代码拉到本地。项目是开源的用git clone或者直接下载压缩包都行。建议放在一个路径不含中文和空格的目录下这一点在Windows上尤其重要某些构建工具对中文路径的处理很不友好。第二步安装依赖。需要本机有Node.js环境版本不要太新也不要太老具体看仓库的说明文档。进入项目目录后执行npm install这一步是最容易出问题的地方。常见报错有三类一是网络原因导致包下载失败可以配置镜像源重试二是某些依赖带原生模块需要本机有编译工具链Windows上可能要装构建工具Mac上要装Xcode命令行工具三是Node版本和依赖要求不匹配报错信息里通常会写清楚需要什么版本照着换就行。第三步启动开发服务npm start启动成功后终端会打印一个本地地址通常是类似http://localhost:8080的形式浏览器打开就能用。如果端口被占用工具一般会自动换一个端口注意看终端输出。提示不同时期的分支启动命令可能略有差异有的用npm start有的用npm run dev。装依赖之前先翻一眼仓库根目录的说明文件和package.json里的scripts段能省掉不少试错时间。部署完成后你得到的是一个完全跑在本机的版本数据不上传断网也能打开界面底图除外。对于内网环境这已经是可用的状态了。3.3 部署后的底图与内网适配本地部署完很多人会碰到一个尴尬情况界面能打开但地图区域是灰的。原因是底图瓦片仍然指向外部服务内网访问不到。解决思路是把底图源换掉。代码里通常有一个配置文件或者一处样式定义写着底图的地址模板。把它改成内网可访问的瓦片服务地址即可。如果内网暂时没有瓦片服务也可以先用离线方式把需要范围的瓦片预先下载好用本地静态服务发布出来再把地址指过去。瓦片按层级和行列号组织目录结构是固定的用任意静态服务器都能发布。另一种更轻的做法是换用矢量底图或者纯色底图。如果只是看数据分布不需要道路和地名把底图换成一张浅色底图甚至空白背景反而更清爽也彻底摆脱了外部依赖。我在内网环境里经常这么干配一个简单的网格或者行政区轮廓作为参照够用且稳定。还要注意一点本地部署的版本保存功能通常是下载到本地文件而不是存到某个云端。这对内网是好事但也意味着协作时要靠文件传递别指望有版本管理。如果团队多人使用建议约定好文件命名规范避免出现一堆untitled.geojson。4. ArcGIS与GeoJSON的双向转换实操4.1 从ArcGIS Pro导出GeoJSON的完整流程ArcGIS Pro从较新的版本开始原生支持GeoJSON导出路径在转换工具箱里的JSON相关工具下工具名是要素转JSON勾选输出为GeoJSON的选项即可。整个流程不复杂但有几个参数决定了导出结果能不能用。第一个关键点是输入数据的坐标系。工具会按照输入图层的坐标系输出坐标值如果你没做处理导出的就是投影坐标单位是米数值是六七位数。这样的文件在GeoJSON查看器里打开必然错位。正确做法是在导出前先把图层投影到WGS84地理坐标系或者用投影工具做一次转换生成一个临时图层再导出。第二个关键点是字段筛选。工具默认会把所有属性字段都带上包括系统维护的OBJECTID、Shape_Length、Shape_Area这类字段。这些字段对下游没用还占体积建议在工具的字段参数里手动勾选需要的字段。特别是Shape_Length和Shape_Area它们是按当前坐标系计算的一旦坐标系变了就失去意义留着反而误导人。第三个关键点是精度。有些版本提供坐标精度参数默认值可能会对坐标做四舍五入。对于一般展示用途保留六位小数足够约十厘米量级如果做精确量算建议保留更多位数或者不设限制。导出完成后别急着交付。用文本编辑器打开文件看三件事开头是不是FeatureCollection、coordinates里的数值范围是否在经纬度合理区间、properties里的字段是否符合预期。这三眼扫下来能拦住大部分低级错误。4.2 导入GeoJSON到ArcGIS反过来的流程也不复杂工具叫JSON转要素输入GeoJSON文件路径指定输出位置和坐标系跑完就得到一个要素类。这里有个容易忽略的点输出坐标系的选择。工具默认可能会用当前地图的坐标系如果你的地图是投影坐标系导入的点会被自动转换过去看起来是对的但如果地图坐标系设置有问题或者数据本身坐标系声明缺失转换结果就可能出现偏移。我的习惯是明确指定输出为WGS84导入成功后再按项目需要做投影这样每一步都可控。另一个坑是几何类型混装。前面说过GeoJSON允许一个文件里混着点和面但ArcGIS的要素类一个只能装一种几何。工具遇到混装文件时的处理方式各版本不一有的只取第一种几何有的直接报错。稳妥做法是先确认文件里的几何类型是否统一不统一就拆成多个文件分别导入。还有个细节是字段名长度和字符。GeoJSON的字段名可以很长可以带空格和特殊符号但导入到地理数据库后字段名会被截断或者替换。如果后续要做字段关联最好在GeoJSON阶段就把字段名规整成短英文名。4.3 坐标系转换的参数与判断方法坐标系是这条链路上最容易出事的地方值得单独展开。先说判断。拿到一份GeoJSON怎么知道它的坐标到底是不是WGS84看数值范围。经度在-180到180之间纬度在-90到90之间且小数位数较多基本就是经纬度。如果看到数值是几十万、几百万那一定是投影坐标单位是米。如果数值在合理范围内但位置明显不对可能是顺序反了也可能是用了某个地方坐标系但数值恰好落在范围内——这种情况比较少见但确实存在。再说转换。从投影坐标转到WGS84需要知道源坐标系是哪一个。国内的成果数据常见的是各类高斯克吕格投影或者墨卡托投影具体带号和中央经线必须搞清楚。中央经线判断错位置会整体平移几百公里这种错误在图上很明显但如果只对着坐标数字看很容易漏掉。转换工具方面ArcGIS的投影工具、开源GIS软件、以及各类坐标转换库都能做。参数上要注意两点一是基准面转换现在的成果数据多是2000国家大地坐标系它和WGS84在厘米级基本一致常规业务可以直接按一致处理但如果遇到更早期的坐标系成果就需要一套转换参数参数不对会带来米级的系统偏差。二是转换方向别搞反从投影到地理是“投影转地理”从地理到投影是反方向工具里的选项要看清楚。换算过程我一般会做个交叉验证挑一个已知位置的特征点转换后在地图上目视检查落在正确位置才算过。纯靠数字核对太容易自欺欺人。4.4 属性字段的类型陷阱从ArcGIS导出到GeoJSON属性字段的类型会发生转换这里面有几个固定的坑。日期字段会变成毫秒时间戳。本来的日期在JSON里成了一个长整数前端拿到如果不做处理界面上就是一串数字。解决办法要么在导出前把日期转成字符串要么在下游统一做一次时间转换。数值字段的精度可能丢失。浮点数在序列化时会保留一定位数某些情况下会出现类似0.30000000000000004这样的结果。对展示影响不大但如果做精确比较就会出问题。空值处理不一致。地理数据库里的空值导出后可能是null也可能是空字符串还可能是字段直接消失。下游代码如果按固定字段读取遇到字段消失就会报错。建议在解析时用带默认值的读取方式别假设字段一定存在。长文本可能被截断。某些导出流程对字符串长度有限制超长文本会被砍掉。如果属性里有备注、描述这类长文本导出后要抽查几条确认内容完整。5. 排错实录那些让人抓头的现场5.1 加载空白或报错的五种原因把文件拖进查看器地图上什么都没有这是最常见的一类问题。我按出现频率排了个序。第一种JSON语法错误。多一个逗号、少一个引号、括号不配对都会让整个文件解析失败。用编辑器的JSON校验功能或者随便找个在线校验器几秒钟就能定位。这类错误通常在手工拼接或者脚本生成时出现。第二种顶层结构不对。有的工具只认FeatureCollection你给一个单独的Feature或者一个裸的Geometry它就不显示。检查文件最外层的type字段。第三种坐标顺序反了。前面详细说过现象是要素跑到赤道或者大洋上。看数值范围就能判断。第四种坐标系不是经纬度。投影坐标直接塞进文件数值超出合理范围有的解析器会默默不画有的会画出诡异的形状。第五种几何无效。比如面的环没有闭合首尾坐标不一致、自相交、坐标点数量不足。这类问题解析器不一定报错但渲染时可能直接跳过。排查顺序建议从语法开始再到结构最后到坐标。因为语法错误会导致后面所有检查都无法进行先解决它效率最高。5.2 位置偏移的定位思路偏移比空白更麻烦因为数据“看起来是对的”只是位置不对。定位思路是判断偏移的性质。如果整体偏移量很大且方向一致大概率是坐标系用错了比如把投影坐标当经纬度或者用错了中央经线。这种情况要做投影转换不能靠平移修补。如果偏移量很小几米到几十米可能是基准面差异或者转换参数不精确。业务上如果对精度要求不高可以接受要求高就得找回正确的转换参数。如果要素之间的相对位置也乱了比如本该相邻的两个面出现了错位或者重叠那可能是数据在生产阶段就有问题或者是坐标顺序局部错乱。这种情况得回到源头数据核查。如果位置在南北或者东西方向上有规律地翻转重点查经纬度顺序。我一般的做法是找一个确定位置的参照物比如某个形状特征明显的路口或者建筑轮廓把它和底图对齐看偏移的方向和量级再反推是哪一环出的问题。比对着坐标数字瞎猜有效得多。5.3 中文与特殊字符乱码中文乱码的根源几乎都是编码。GeoJSON文件应该是UTF-8编码不带BOM。带BOM的文件在某些解析器里会在开头多出一个不可见字符导致JSON解析直接失败报错信息还很模糊只说“意外的字符”。ArcGIS导出的文件有时会带BOM尤其是通过某些中间环节转换后。解决办法是用编辑器另存为无BOM的UTF-8或者用脚本读进来重新写一遍。另一种乱码出现在属性值里显示成一堆问号或者方块。这通常是文件本身编码不对比如用了本地编码保存。统一转成UTF-8即可。还有一种情况是字体问题属性值里包含生僻字或者特殊符号地图上的标注显示不出来。这不是数据问题是渲染环境的字体覆盖不全换字体或者改用图片标注能缓解。5.4 大文件卡顿的优化手段要素一多浏览器就吃不消。优化有几个层次按投入产出比排序。最直接的是精简属性。很多文件的体积大头在properties把不参与展示的字段删掉体积能降一大截。这一招几乎无成本效果立竿见影。其次是降低坐标精度。保留六位小数对绝大多数展示场景足够把多余的位数砍掉每个坐标能省好几个字符。要素多的时候这个收益很可观。再进一步是简化几何。对线状和面状要素做抽稀减少节点数量同时控制误差在可接受范围内。这里要注意简化会改变形状用于展示没问题用于量算就不合适了。如果还不行就得考虑分块加载。按空间范围或者属性把数据切成多份按视图范围动态加载。这已经属于工程改造不是单纯的文件优化了。最后的手段是换格式。GeoJSON本身是文本格式冗余天生就大。同样的数据换成二进制格式或者切片体积和渲染性能都会有数量级的改善。判断标准很简单如果你的数据已经到了需要“优化”的程度那它可能本来就不该用GeoJSON承载。5.5 常见问题速查表把上面这些零散的经验收拢成一张表遇到问题时按症状查能省不少时间症状表现最可能的原因快速验证方法处理方向打开后地图空白控制台报解析错误JSON语法错误或带BOM用编辑器格式化看是否报错修复语法另存为无BOM的UTF-8要素全部集中在赤道附近经纬度顺序写反检查坐标数值范围交换坐标数组前两位要素位置整体偏移数百公里坐标系不是WGS84看坐标值是否超过180做投影转换后重新导出要素形状扭曲、拉伸异常投影参数或中央经线错误核对源数据的坐标系定义找回正确投影参数重新转换中文属性显示为问号或方块文件编码不是UTF-8用十六进制查看文件头转码为UTF-8无BOM日期字段显示为一串数字导出时转为毫秒时间戳看字段值是否为13位整数下游做时间格式化面状要素渲染残缺或有空洞环未闭合或自相交检查首尾坐标是否一致修复几何后重新导出文件加载后浏览器卡死要素量过大看文件体积和要素计数精简属性、降精度、切分数据导入GIS软件后字段缺失字段名超长或含特殊字符对比导出前后的字段列表规整字段名为短英文名同一个文件在不同工具里位置不同某工具忽略了坐标系声明换两个工具交叉验证统一转换为WGS84后再使用这张表里我特别想强调最后一行。“同一个文件在不同工具里显示位置不同”这种情况根源往往是某些工具对非标准坐标做了宽容处理而另一些严格按规范解析。遇到这种分歧不要纠结谁对谁错直接把数据统一到WGS84让所有工具都没得争。最后分享一个我自己养成的小习惯任何GeoJSON文件交付前都用文本编辑器打开扫一眼开头两百行确认顶层结构、坐标量级、字段命名这三件事。这个动作花不了一分钟但能拦下我遇到过的大多数返工。数据这东西出事往往不是因为难而是因为跳过了最简单的检查。