ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GeoJSON数据处理指南:从zip解压到入库的完整避坑手册

GeoJSON数据处理指南:从zip解压到入库的完整避坑手册 简介这是一份搜集整理的GeoJSON地理数据合集主要面向Web前端开发者、GIS分析人员及地图可视化爱好者用于解决地理边界与地名坐标数据获取、格式转换不便的问题。GeoJSON基于JSON结构支持点、线、面等几何类型适合在浏览器端通过JavaScript直接解析渲染。压缩包共1102个文件以1100个json格式的地理数据为主另附1个csv坐标属性表和1个md说明文档整体体积约156.12MB。数据覆盖全球多个国家与地区的行政区划、边界和经纬度信息包括印度尼西亚、巴西、菲律宾、加拿大等不同层级的省级/市级GeoJSON可直接用于地图渲染、空间查询、数据统计或前后端地理数据交互实验。目前已有584人学习下载。该合集省去了逐平台检索与格式转换的繁琐步骤提供现成的开源数据便于快速搭建交互式地图原型、验证GeoJSON解析流程或者作为教学与项目开发的基础数据集md文档也能帮助理解文件结构与属性字段。1. 搜集来的GeoJSON数据打包成zip比你想的更值得认真对待拿到一个名为“搜集来的geojson数据_GeoJSON_data.zip”的压缩包很多人的第一反应是解压、拖进地图工具、看一眼就跑。但作为常年跟地理数据打交道的人我劝你停一下这个zip里装的不是普通文件而是一批带有空间坐标的GeoJSON数据。GeoJSON已经成为Web地图、GIS分析、数据可视化项目里最通用的交换格式但正因为通用也最容易在“搜集”过程中被悄悄污染——坐标系混乱、编码错误、要素重复、几何非法随便一个都能让后续的渲染和分析结果变成一坨玄学。这篇文章就把我从解压到入库的完整路径讲清楚覆盖文件校验、编码转换、坐标检查、合并去重、避坑排查以及最后怎么把它变成能快速查询的本地数据源。适合正在做地图可视化、空间数据分析、爬虫数据清洗或者刚拿到一堆陌生GeoJSON包准备二次开发的从业者照着走能省下半天调试时间。2. 拆开GeoJSON_data.zip之前先搞懂GeoJSON的结构与坐标系2.1 GeoJSON的数据组织Feature、Geometry、Properties三件套GeoJSON不是一种文件格式而是一种基于JSON的空间数据编码规范。打开任何一个合法的GeoJSON文件最外层通常是一个对象它有一个type字段取值可能是FeatureCollection、Feature、Point、LineString、Polygon等。日常搜集到的数据九成是FeatureCollection里面是一个features数组每个元素都是一个Feature对象。一个标准的Feature长这样{ type: Feature, geometry: { type: Point, coordinates: [116.397, 39.908] }, properties: { name: 故宫, level: cultural } }注意geometry里的coordinates并不是随便填的。对Point来说它是一个两元素或三元素数组分别代表经度、纬度可选海拔对LineString和Polygon则是一个嵌套数组每一层的层级对应坐标轴数量。很多时候报错就出在“多拐了一层”或“少拐了一层”例如把Polygon的坐标写成了[[[116.3,39.9], [116.4,39.9], ...]]但实际应该是[ [ [x,y], [x,y], ... ] ]第一个内层数组代表外环后面可跟多个洞。写代码处理时我一般会先递归检查嵌套深度而不是直接取[0][0]否则遇到空坐标或异形多边形时直接翻车。properties里面是属性字段理论上任意JSON类型都可以但实际因GeoJSON引擎而异。比如Leaftlet可以读取properties里的任意字段做弹窗而Mapbox GL则要求属性值必须是字符串、数字或布尔值不能嵌套对象。所以拿到压缩包里的文件第一步不是看地图长得怎么样而是用jq或Python把properties的键名全部拉出来看看有没有会让你前端渲染崩坏的复杂类型。2.2 坐标系与精度为什么同一个点在不同软件里会飘GeoJSON规范里默认使用WGS-84坐标系也就是EPSG:4326经纬度坐标单位是十进制度。但现实很骨感很多从第三方“搜集”来的数据坐标可能来自GCJ-02火星坐标、BD-09百度坐标甚至更冷门的UTM投影坐标。如果你直接把UTM的七位数坐标当成经度丢进Web地图点位会飞到非洲。判断坐标系最直接的方式是看数值范围——经度正常在-180到180纬度在-90到90如果看到x452124.32, y4412345.67这种六到七位数基本就是投影坐标必须先做反投影。精度问题同样容易被忽略。GeoJSON里的浮点数经常出现117.23041532784142这样的长尾这通常是从Shapefile或CAD文件转出来时保留了原始精度。对点数据来说0.00001度大约对应1米超出实际采集精度的位数除了撑大文件体积没有任何意义。我一般会在清洗阶段统一把经纬度四舍五入到6位小数约0.1米精度文件体积能缩水20%-30%同时不影响后续空间分析。下面这个Python脚本是mini版“GeoJSON体检”直接拿来检查压缩包里所有文件的坐标系是否可疑import json, glob, os for f in glob.glob(*.geojson): with open(f, encodingutf-8) as fp: data json.load(fp) for feat in data.get(features, []): coords feat[geometry][coordinates] # 递归取所有坐标点 pts [] def walk(c): if isinstance(c[0], (int, float)): pts.append(c[:2]) else: for item in c: walk(item) walk(coords) for lon, lat in pts[:10]: # 每个文件看前10个点就够 if lon 180 or lon -180 or lat 90 or lat -90: print(f{f}: 疑似非WGS84坐标示例点({lon},{lat})) break这段脚本的思路是递归进入coordinates结构取每个点坐标的前两个值判断范围。参数说明pts[:10]限制只取前10个点避免文件过大时遍历所有点导致程序卡死walk函数递归处理任意深度的坐标嵌套不管是Point还是MultiPolygon都能统一处理。如果某个文件被判定为疑似非WGS84后续就要单独处理坐标系转换千万不能跟其他文件混着合并。3. 用命令行把GeoJSON数据zip包整理成可用的本地数据源3.1 快速解压与校验文件完整性拿到zip后我先不急着双击解压。Windows资源管理器自带解压对普通文件还好但批量处理GeoJSON时我更推荐命令行因为可以顺便做完整性校验。zip本身有CRC32校验码unzip命令在解压时会自动比对如果文件损坏会直接报错而图形化工具往往会静默解出一个残缺文件坑非常大。在Linux或macOS上unzip -l GeoJSON_data.zip # 只列内容不解压 unzip -t GeoJSON_data.zip # 测试所有文件的完整性 unzip -o GeoJSON_data.zip -d data # 解压到data目录-o表示覆盖参数说明-l是list先看压缩包里到底有什么防止解压出同名目录覆盖已有文件-t是test逐文件读取并比对CRC任何损坏都会在这里暴露-d指定输出目录强烈建议养成“解压到独立目录”的习惯不要散落一堆GeoJSON在桌面或项目根目录。如果unzip -t输出里有bad CRC或者mismatch这个文件就是坏的直接放弃它不要幻想能用修复工具救回来。如果是Windows且没有unzip可以用PowerShell的Expand-Archive但那个不测试完整性所以我会先跑一遍Get-FileHash对比来源方提供的哈希值——但搜集来的数据往往没有源哈希那就只能用unzip -t硬测。3.2 批量转码为UTF-8并检查GeoJSON合法性GeoJSON标准要求文件使用UTF-8编码但很多从GIS软件直接导出的文件实际是GBK或GB2312尤其属性字段里有中文时更容易踩坑。表现就是解压后在记事本里看没问题但用Python读出来全是乱码或者加载到网页地图后属性名变成一堆“锟斤拷”。解决办法是批量转码这里用iconv最顺手for f in data/*.geojson; do # 先用file判断编码常见的是UTF-8和GBK encoding$(file -b --mime-encoding $f) if [ $encoding ! utf-8 ] [ $encoding ! us-ascii ]; then iconv -f $encoding -t UTF-8 $f $f.tmp mv $f.tmp $f fi done这段脚本的逻辑file -b --mime-encoding输出文件实际编码常见的有utf-8、gbk、iso-8859-1等。如果不是UTF-8ASCII是UTF-8子集不用转就用iconv转换。注意iconv遇到非法的输入序列会直接报错并生成半截文件所以要用确保转换成功后再替换原文件防止原文件被清空。如果你在Windows上不想装GNU工具可以用Python的chardet库识别编码但准确率不是100%遇到混合中文编码时还是要人工抽查。转码后还要验证GeoJSON合法性。json.load只能保证是合法JSON不能保证是合法GeoJSON。简单起见用一个在线验证器或本地库我常用的是Python的geojson包import geojson from pathlib import Path for p in Path(data).glob(*.geojson): with open(p, encodingutf-8) as f: try: geojson.load(f) geojson.validate(f) # 注意geojson库的validate是旧API需重新读文件 print(f{p.name}: OK) except Exception as e: print(f{p.name}: {e})注意这里有个坑geojson.load(f)后文件指针已经到末尾接着调用geojson.validate(f)会读到空内容所以必须重新打开文件或者先读字符串再解析。正确写法是text p.read_text(encodingutf-8) data geojson.loads(text) geojson.validate(data)geojson.validate会检查type枚举、coordinates层级、Feature必填字段等比裸json.load强得多。如果validation报出TypeError或者ValueError基本就是坐标数组层级不对比如Polygon的coordinates是三维数组但数据里只有二维那个文件后续处理肯定成问题建议直接隔离。3.3 用Python脚本统一合并多个GeoJSON文件搜集来的zay包里很可能有几十个按区域或时间切割的小GeoJSON文件直接拿给前端用会有大量HTTP请求合并成单个FeatureCollection是常规操作。但合并不是简单拼接features数组还要处理重复要素和属性不一致。下面是一个可复用的合并脚本import json, sys from pathlib import Path def merge_geojson(input_dir, output_file, dedupe_keyNone): all_features [] seen set() for p in Path(input_dir).glob(*.geojson): with open(p, encodingutf-8) as f: data json.load(f) if data.get(type) ! FeatureCollection: # 如果是单个Feature包一层如果是Geometry跳过并告警 if data.get(type) Feature: all_features.append(data) else: print(f跳过非FeatureCollection: {p.name}) continue for feat in data.get(features, []): if dedupe_key: key json.dumps(feat.get(properties, {}).get(dedupe_key), ensure_asciiFalse) else: key json.dumps(feat.get(geometry), sort_keysTrue) if key in seen: continue seen.add(key) all_features.append(feat) merged {type: FeatureCollection, features: all_features} with open(output_file, w, encodingutf-8) as f: json.dump(merged, f, ensure_asciiFalse) print(f合并完成: {len(all_features)} 个要素 - {output_file}) if __name__ __main__: merge_geojson(data, merged.geojson, dedupe_keyid)参数说明dedupe_key用于指定属性字段作为去重依据如果原始数据有唯一ID如id、fid、osm_id用这个字段最可靠如果没传脚本默认用整个geometry对象做去重键但这只对完全相同的要素有效两个坐标有微小差异的重复点不会被去重。json.dumps中的sort_keysTrue是为了让相同的坐标即使键顺序不同也能识别。ensure_asciiFalse保证输出文件里的中文是明文而不是\uXXXX转义序列方便其他工具读取。合并后一定要检查要素数量通常源文件里会有断头数据。靠len(all_features)输出能看出有没有明显暴增或骤减。如果发现合并后数量比所有源文件数量之和还大说明有FeatureCollection嵌套了重复数组如果小很多说明去重键选得太宽把不该去重的也吞了。这个脚本建议在项目里保存以后每次拿到新的GeoJSON包都能复用。4. 处理zip中的GeoJSON数据避坑与常见问题排查4.1 现象中文属性乱码QGIS里正常但浏览器里是“锟斤拷”原因文件实际是GBK或GB2312编码但GeoJSON规范要求UTF-8。QGIS等桌面GIS对编码有自动嗅探能力能容忍错误编码而Web引擎按UTF-8解析导致中文全乱。解决按第3.2节的方式批量转码。如果转码后仍有乱码多半是源文件里存在CP936和UTF-8混合内容iconv会把已经UTF-8的段落再次误转。这时用Python的errorsreplace策略逐字节清洗或者把文件读为二进制按UTF-8解码失败后改用GBK尝试。我见过一个文件里同一个属性值前面是“北京市”后面是“\u542f\u718a”这种JSON转义这种只能写正则把\uXXXX还原别无他法。4.2 现象QGIS能打开但自己写的Python脚本说JSONDecodeError原因文件带有UTF-8 BOM字节顺序标记或者行尾是CRLF甚至文件头有不可见字符。Python的json.load默认不接受BOM需要指定encodingutf-8-sig读取。解决统一在读取时使用utf-8-sig或者先跑一次sed -i 1s/^\xEF\xBB\xBF// file.geojson去掉BOM。另外注意如果是Windows上用记事本另存过行尾会变成CRLF这不影响JSON解析但会影响某些命令行工具的行号定位。我习惯在清洗阶段把所有GeoJSON统一用Python重新写出一次写成LF、无BOM彻底杜绝后续问题from pathlib import Path for p in Path(data).glob(*.geojson): raw p.read_bytes() # 去掉BOM if raw.startswith(b\xef\xbb\xbf): raw raw[3:] # 统一换行符为LF text raw.decode(utf-8).replace(\r\n, \n) p.write_text(text, encodingutf-8, newline\n) print(f清理完成: {p.name})4.3 现象文件上百MB页面加载卡死浏览器直接崩溃原因几何坐标精度太高动辄15位小数导致压缩包解压后文件膨胀同时可能存在大量冗余几何点比如一条线段中间隔0.5米就采一个点实际0.1公里一个点也够。解决使用simplify算法抽稀同时降低坐标精度。shapely库的simplify是最常用方案from shapely.geometry import shape, mapping from shapely.geometry.base import BaseGeometry import json def simplify_geojson(infile, outfile, tolerance0.0001): with open(infile, encodingutf-8) as f: data json.load(f) for feat in data[features]: geom shape(feat[geometry]) simplified geom.simplify(tolerance, preserve_topologyTrue) feat[geometry] mapping(simplified) with open(outfile, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse) # tolerance0.0001 约等于10米适合市级尺度参数说明tolerance是简化容差单位与坐标相同。对经纬度数据0.0001度约为10米如果要精细到街区用0.00001约1米。preserve_topologyTrue保证简化后不会出现自相交等几何拓扑错误虽然速度稍慢但对于底图数据值得。如果文件里只有点数据简化没有意义那就只做坐标舍入[round(x,6) for x in coord]。4.4 现象zip文件解压时提示“伪加密”或“CRC校验失败”原因搜集来的zip包可能是某些人用工具强行修改过文件头把普通zip标记为加密但实际没有加密内容也可能是文件在传输过程中损坏。Windows资源管理器对伪加密文件会直接弹框要求输入密码此时不要瞎试密码先查一下文件头。一个有效的排查方法是用zipinfo查看zipinfo -v GeoJSON_data.zip | grep -i encrypt如果输出显示file has no encryption但解压又要密码那基本就是伪加密修掉加密标志位即可。常见做法是用脚本把general purpose bit flag的第0位清零。这类文件如果真的损坏且没有备份修复概率极低所以我会建议做数据源的时候保留一份原始zip在冷存储不要只留解压目录。伪加密是恶意或者打包工具bug产生正常也不会遇到遇到了直接换一个来源重新下载比修复可靠。4.5 现象合并后地图上要素位置偏移了好几百米原因某个源文件使用了GCJ-02坐标系而其他文件是WGS-84。这种“坐标漂移”肉眼可能在城市尺度下看不太出来但叠加到道路或建筑边界时就暴露了。解决识别出GCJ-02文件单独做坐标纠偏。GCJ-02和WGS-84之间的转换没有公开的数学公式常用方法是查表或者用反向偏移算法。这里给一个常见的纠偏片段有效范围是中国大陆注意不要用于其他地区import math def gcj02_to_wgs84(lng, lat): # 简化版先转火星坐标再反算精度约1米 a 6378245.0 ee 0.00669342162296594323 dlat _transform_lat(lng - 105.0, lat - 35.0) dlng _transform_lng(lng - 105.0, lat - 35.0) radlat lat / 180.0 * math.pi magic math.sin(radlat) magic 1 - ee * magic * magic sqrtmagic math.sqrt(magic) dlat (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * math.pi) dlng (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * math.pi) return lng dlng, lat - dlat注意这是一个逆推近似实际工程里我们一般用pyproj或coord_convert库精度更高。这段代码仅用于让你理解原理坐标漂移是系统性的不是随机噪声所以可以通过统一转换消除。在写批量处理脚本时我一般先用几个已知城市的地标点手工比对坐标确定偏移方向再决定是否全部转坐标系。5. 把整理后的GeoJSON zip变成可查询的本地数据库一个提速技巧数据清洗完如果只是做一次性可视化merged.geojson就够了。但如果是做Web服务或者反复查询某个区域内的要素每次全量加载GeoJSON都是对CPU和内存的浪费。我的习惯是把它转成SQLite Spatialite或者直接引入tippecanoe切成矢量瓦片。以SQLite为例用ogr2ogr一行命令就能把GeoJSON导入到Spatialiteogr2ogr -f SQLite output.db merged.geojson -nln poi -lco SPATIALITEYES -lco SRID4326导入后可以用SQL直接做空间查询比在Python里遍历GeoJSON快两个数量级。比如查故宫周围3公里的POISELECT name FROM poi WHERE ST_DWithin(geom, ST_GeomFromText(POINT(116.397 39.908), 4326), 0.03);这里的0.03对应约3公里因为单位是度。如果你不需要空间索引也可以只把properties展开成普通表用普通SQL过滤。对于凌乱的数据源这个转换本身又是一次强校验——ogr2ogr会跳过无法解析的要素并输出警告日志你正好可以把日志留下来当清洗清单。我自己的习惯是最后跑一遍数据条数核对源zip里每个文件的行数、合并后的总条数、入库后的总数三者对不上就说明中间有丢失宁可回头修数据也不带着脏数据上线。数据这条路整洁比数量重要一个坏坐标比少一条记录头疼得多。希望这篇清单一套走下来能让你拿到“搜集来的geojson数据_GeoJSON_data.zip”这类包时不再心里发虚半小时之内整理成能直接投入使用的本地数据源省下的时间多睡会儿都比对着乱码抓头发强。希望帮到你。本文还有配套的精品资源点击获取
返回列表