ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

世界地图GeoJSON实战:从数据校验到前端可视化

世界地图GeoJSON实战:从数据校验到前端可视化 简介这是一份面向地理信息开发、数据可视化与GIS教学场景的世界国家边界数据集以GeoJSON与JSON格式提供全球各国及部分地区的矢量边界适合需要快速搭建世界地图、做区域统计或离线地图应用的前端与后端开发者。压缩包共约2000个文件以3443个json文件为主整体仅2.1MB体积轻量便于随项目分发每个文件对应一个国家或地区内部采用Feature Collection结构包含Polygon与MultiPolygon几何对象、WGS84经纬度坐标以及国家名称等属性字段可直接被Leaflet、Mapbox GL JS等地图库加载。目前已有4730人学习下载说明其在同类资源中具备一定参考价值。借助这些数据读者可完成交互式世界地图渲染、按国家边界做空间统计、地理编码定位以及服务端地理信息API搭建也能作为GIS课程与地图制作练习的基础素材帮助理解现代地图服务的数据组织方式。1. 世界地图 GeoJSON 到底解决什么问题从一张“平凡的世界地图示意图”说起做过数据可视化的人多半遇到过这个场景产品经理甩过来一张“平凡的世界地图示意图”要求把各国销售额按国家填色鼠标悬停弹出详情最好还能点击下钻。你打开搜索引擎搜到的要么是几百 MB 的 shapefile要么是某个已经失效的网盘链接要么是一张不能交互的 PNG。真正能直接喂给 ECharts、D3、Mapbox、Leaflet 的其实是世界国家边界数据而 GeoJSON 就是目前最通用的载体格式。它用纯文本描述点、线、面每个国家是一个 Feature属性里带 ISO 代码、中英文名称几何部分是经纬度坐标串。标题里的“各国地图 json 数据”本质就是这套东西一份世界地图 GeoJSON拆开就是各国地图 JSON。它解决的是“我有一堆按国家维度的数据需要一张能编程控制的世界底图”这个问题适合前端可视化、GIS 入门、数据大屏、教学演示这几类人。选它的理由很直接文本可读、体积极小、几乎所有地图库原生支持改一个属性就能换配色不用装任何桌面软件。2. 拿到世界国家 GeoJSON 后先做三件事校验、裁剪、投影2.1 为什么不能直接扔进项目里网上流传的世界地图 GeoJSON 质量参差不齐常见毛病有三个一是坐标系不是 WGS84直接画出来整体偏移二是几何类型混用 Polygon 和 MultiPolygon前端遍历时崩掉三是属性字段命名五花八门有的叫name有的叫NAME有的叫admin。我一般拿到文件后先跑一遍结构校验确认type是FeatureCollection每个 feature 的geometry.type只有Polygon或MultiPolygonproperties里至少有一个能唯一标识国家的字段。这一步不做后面填色、匹配数据全是玄学。import json with open(world.geojson, r, encodingutf-8) as f: data json.load(f) # 1. 顶层类型必须是 FeatureCollection assert data[type] FeatureCollection, 顶层不是 FeatureCollection # 2. 统计几何类型分布混用会导致部分渲染库报错 geom_types {} for feat in data[features]: gt feat[geometry][type] geom_types[gt] geom_types.get(gt, 0) 1 print(几何类型分布:, geom_types) # 3. 找出属性字段确认有唯一标识 sample_props data[features][0][properties] print(属性字段:, list(sample_props.keys()))这段代码做三件事断言顶层结构、统计几何类型、打印属性字段。参数上唯一要注意的是编码GeoJSON 规范要求 UTF-8用gbk打开中文国名会乱码。如果几何类型里出现了GeometryCollection说明这份数据被处理过前端库大概率不支持建议换一份。2.2 按需裁剪只保留你要的国家世界地图 GeoJSON 全量通常在 200KB 到 2MB 之间取决于精度。如果项目只关心亚洲或某几个国家裁剪能显著减小体积。裁剪不是简单删 feature还要注意跨国界的 MultiPolygon比如俄罗斯、美国这种横跨多洲的国家删的时候要按 ISO 代码整条删不能按坐标范围切否则会出现半个国家。# 保留指定 ISO 代码的国家ISO_A3 是常见字段名 keep {CHN, JPN, KOR, SGP} filtered { type: FeatureCollection, features: [ f for f in data[features] if f[properties].get(ISO_A3) in keep ] } with open(asia_subset.geojson, w, encodingutf-8) as f: json.dump(filtered, f, ensure_asciiFalse) print(裁剪后 feature 数:, len(filtered[features]))ensure_asciiFalse保证中文国名原样输出不转成\uXXXX。如果你的数据里没有ISO_A3退而求其次用name字段但要注意重名问题比如“刚果”有两个。裁剪后建议再跑一次 2.1 的校验确认没有空 geometry。2.3 投影为什么你的地图看起来是扁的GeoJSON 存的是经纬度也就是 WGS84 地理坐标。直接画在平面上高纬度地区会被拉伸格陵兰看起来比非洲还大。这不是数据错是投影问题。常见做法是前端用墨卡托投影Web MercatorECharts 的geo组件默认就是。如果你要做等面积对比得在服务端用pyproj转成 Albers 或 Equal Earth再输出 GeoJSON。参数上墨卡托适合网页交互等面积适合统计图表别混用。提示投影转换会改变坐标数值转完后 GeoJSON 不再是 WGS84后续如果还要叠加 GPS 点必须统一坐标系。3. 用 Python 把各国地图 JSON 数据跑通从读取到出图3.1 最小可复现流程GeoPandas 读入并画一张图新手最容易卡在“数据读进来了但画不出来”。我用 GeoPandas 走一遍最小流程它底层依赖 shapely 和 fiona能直接吃 GeoJSON。先装依赖再读文件最后plot()出图。这一步跑通说明数据本身没问题后面换任何前端库都只是渲染层的事。pip install geopandas matplotlibimport geopandas as gpd import matplotlib.pyplot as plt # 读入世界地图 GeoJSON world gpd.read_file(world.geojson) # 打印坐标系和字段确认是 EPSG:4326 print(CRS:, world.crs) print(字段:, world.columns.tolist()) # 画一张最简世界地图 fig, ax plt.subplots(figsize(12, 6)) world.plot(axax, edgecolorblack, facecolorlightgray, linewidth0.3) ax.set_title(World Map from GeoJSON) plt.savefig(world_basic.png, dpi150, bbox_inchestight)read_file会自动识别 GeoJSON 格式crs如果是EPSG:4326说明是标准经纬度。plot的linewidth控制国界线粗细世界地图建议 0.3 到 0.5太粗会糊成一片。facecolor是填充色后面按数据填色就是改这个参数。3.2 按国家数值填色把业务数据挂上去光有底图没用真正干活是把你的数据按国家匹配上去。假设你有一份 CSV字段是iso_a3和sales用merge挂到 GeoDataFrame 上再用choropleth填色。这里的关键是字段类型要一致CSV 里的 ISO 代码如果是小写得先转大写否则匹配不上地图一片灰。import pandas as pd # 模拟业务数据 df pd.DataFrame({ ISO_A3: [CHN, USA, BRA, IND, RUS], sales: [1200, 900, 300, 700, 450] }) # 统一字段名后合并 world world.rename(columns{ISO_A3: iso}) df df.rename(columns{ISO_A3: iso}) merged world.merge(df, oniso, howleft) # 按 sales 填色缺失值用灰色 fig, ax plt.subplots(figsize(14, 7)) merged.plot( columnsales, axax, legendTrue, cmapOrRd, missing_kwds{color: lightgray, label: No Data}, edgecolorblack, linewidth0.3 ) ax.set_title(Sales by Country) plt.savefig(world_choropleth.png, dpi150, bbox_inchestight)howleft保证所有国家都保留没数据的显示灰色。cmap是配色方案OrRd是橙红渐变适合表示数值高低。missing_kwds处理缺失值不设的话没数据的国家会变成白色和背景混在一起。这一步跑通你就有了一个可复用的“数据到地图”的管道。3.3 导出给前端精简字段和坐标精度Python 画图是给自己看的最终要交给前端。前端对体积敏感导出前要做两件事删掉用不到的属性字段降低坐标精度。坐标精度从 6 位小数降到 4 位体积能减 30% 左右视觉上几乎看不出差别。字段只保留name和iso其他全删。import json # 精简属性字段 for feat in merged_features : json.loads(merged.to_json())[features]: props feat[properties] feat[properties] { name: props.get(name), iso: props.get(iso) } # 降低坐标精度 def round_coords(coords, ndigits4): if isinstance(coords[0], (int, float)): return [round(c, ndigits) for c in coords] return [round_coords(c, ndigits) for c in coords] for feat in merged_features: feat[geometry][coordinates] round_coords( feat[geometry][coordinates] ) with open(world_slim.geojson, w, encodingutf-8) as f: json.dump( {type: FeatureCollection, features: merged_features}, f, ensure_asciiFalse, separators(,, :) )separators(,, :)去掉 JSON 里的空格进一步压缩体积。round_coords递归处理嵌套坐标Polygon 和 MultiPolygon 都能覆盖。导出后建议用浏览器打开看一眼确认没有语法错误。4. 避坑与排查世界地图 GeoJSON 最常见的 5 个翻车现场4.1 地图整体偏移或变形现象画出来的地图位置不对或者形状明显扭曲。原因坐标系不是 WGS84常见的是 GCJ-02 或 BD-09这两种在国内地图数据里很常见。解决用pyproj检查crs如果是EPSG:4326以外的先转换再使用。转换命令gpd.read_file(...).to_crs(EPSG:4326)。4.2 国家匹配不上大片灰色现象业务数据挂上去后大部分国家没颜色。原因ISO 代码大小写不一致或者用了ISO_A2和ISO_A3混用。解决合并前统一转大写打印两边的前 10 个值对比。我一般会写一句print(set(df[iso]) - set(world[iso]))直接看差集。4.3 前端渲染卡顿甚至崩溃现象地图加载后浏览器卡死。原因GeoJSON 精度太高单个文件超过 5MB或者几何类型里有GeometryCollection。解决用 3.3 的方法降精度、删字段或者换用 TopoJSON体积能再减 80%。如果还卡考虑按需加载只加载当前视野的国家。4.4 中文国名乱码现象地图上国家名显示成问号或方块。原因文件编码不是 UTF-8或者前端字体不支持中文。解决保存时用encodingutf-8前端确认Content-Type带charsetutf-8。如果还乱检查数据源本身是不是 GBK。4.5 跨国界国家被切成两半现象俄罗斯、美国、法国等国家只显示一部分。原因裁剪时按坐标范围切把 MultiPolygon 切断了。解决按 ISO 代码整条保留或删除不要按坐标过滤。如果必须按区域裁用shapely的intersection但要注意保留 MultiPolygon 结构。5. 进阶技巧用 TopoJSON 和按需加载把世界地图压到 100KB 以内5.1 TopoJSON共享边界只存一次GeoJSON 的痛点是相邻国家的边界存了两遍比如中俄边境中国存一遍俄罗斯存一遍。TopoJSON 用弧段共享同样的世界地图体积能减到 GeoJSON 的 20% 左右。转换用topojson命令行工具或者 Python 的topojson库。转换后前端用topojson-client还原成 GeoJSON 再渲染API 几乎不变。# 安装 topojson 工具 npm install -g topojson-server topojson-simplify # GeoJSON 转 TopoJSON并做简化 geo2topo world.geojson world.topo.json toposimplify -s 0.01 world.topo.json world.simple.topo.json-s 0.01是简化阈值数值越大越简但形状失真也越明显。世界地图建议 0.01 到 0.05 之间自己试。转换后体积通常从 1MB 降到 200KB 以内。5.2 按需加载只请求当前视野的国家如果项目是交互式地图没必要一次加载全世界。做法是把世界地图按大洲拆成多个 GeoJSON用户缩放到哪个洲就加载哪个。拆分用geopandas按continent字段分组导出前端监听zoomend事件动态请求。这样首屏体积能压到 100KB 以内加载速度肉眼可见地快。# 按大洲拆分导出 for continent, group in world.groupby(continent): group.to_file(fworld_{continent}.geojson, driverGeoJSON) print(continent, len(group), countries)groupby的字段名要按你的数据实际字段改常见的是continent或region。导出后每个文件单独校验确认没有空 geometry。5.3 一个我常犯的错误早期做数据大屏我图省事直接用了网上下的世界地图 GeoJSON没校验坐标系结果中国区域整体往东偏了几百公里和业务点位完全对不上。排查了一下午才发现那份数据是 GCJ-02。从那以后我拿到任何 GeoJSON 第一件事就是print(gdf.crs)不是EPSG:4326就先转再干活。这个习惯帮我省了至少三次返工。世界地图 GeoJSON 本身不复杂坑都在数据来源和坐标系上把这两点盯住后面就是纯粹的渲染和交互。希望帮到你。本文还有配套的精品资源点击获取
返回列表