简介:这份东南亚地图SHP格式数据包面向GIS从业者、地理科研人员及城市规划学习者,提供覆盖东南亚区域的基础矢量底图,可用于地图制作、边界分析、缓冲区与网络分析等场景。压缩包共53个文件,约15.05MB,以shp、shx、dbf、prj、sbn、sbx等Shapefile标准组件为主,并附带xml元数据与aih、ain索引文件,分别承担几何图形、属性表、投影信息与空间索引等职能,便于在ArcGIS、QGIS等平台直接加载。数据内容涵盖东南亚国家边界、省界、河流、水域及主要城市驻地,可支撑区域政治经济研究、行政区划统计、水资源与防洪规划、城市分布与交通布局等分析。目前已有2433人学习下载,适合需要快速获取东南亚基础地理底图、开展空间分析与专题制图的用户参考使用。
1. 东南亚地图shp格式:从一份省界河流数据说起
做区域地理分析的人多半遇到过这种局面:手头有一份东南亚的行政区划需求,要画省界、标主要城市驻地、叠河流水域,结果翻遍硬盘只找到一份分辨率感人的全球底图,省界糊成一团,河流断断续续。东南亚这块地方尤其麻烦,中南半岛和马来群岛加起来十一个国家,海岸线碎、岛屿多、跨境河流密,随便一份粗数据放上去,出图效果直接翻车。这次拆的这份东南亚地图shp格式资源,核心就是解决这个痛点——它把省界、河流、水域、主要城市驻地这几类矢量要素整理成了标准 shapefile,能直接拖进 QGIS、ArcGIS 或者用 GeoPandas 读,省掉自己从 OSM 一点点抠数据的时间。适合做区域规划、物流选址、环境分析、教学出图的从业者,也适合刚接触空间数据、想拿一份结构干净的东南亚矢量练手的新手。下面按「数据里到底有什么 → 怎么读进来 → 怎么用对 → 坑在哪」的顺序拆一遍。
2. 拆开这份shp:图层结构、坐标系与字段设计
2.1 四类要素分别对应什么业务场景
拿到一份 shapefile,第一件事不是急着加载,而是先搞清楚它由哪些图层组成。这份资源按要素类型拆成了省界、河流、水域、主要城市驻地四组,每组通常是一个独立的 .shp 文件配一套同名辅助文件。省界图层是面数据,用来做行政区划统计、区域着色、按省聚合;河流图层是线数据,适合做流域分析、沿线缓冲、路径参考;水域图层是面数据,涵盖湖泊、水库、近岸水体这类面状要素,做淹没分析或者水域面积统计时用得上;主要城市驻地是点数据,带城市名称字段,用来做标注、做点位分布、算城市间距离。
这四类分开存而不是塞进一个文件,是有道理的。面、线、点混在一个图层里,属性表字段对不齐,渲染时样式也没法统一控制。分开之后,每个图层只保留自己需要的字段,读进来干净,后续做空间连接也省事。常见做法是省界和城市驻地配合用,河流和水域配合用,前者管行政,后者管自然地理,两条线互不干扰。
2.2 坐标系与编码:别让偏移和乱码毁掉一下午
shapefile 本身不带坐标系信息,靠的是同目录下的 .prj 文件。这份数据如果 .prj 里写的是 WGS84(EPSG:4326),那经纬度直接就是 GPS 坐标,做全球叠加没问题;如果写的是某个投影坐标系,比如 UTM 分区,那算面积、算距离会更准,但和其他 WGS84 数据叠加时就得先转换。判断方法很简单,用 ogrinfo 看一眼:
# 查看 shp 的坐标系和字段结构,不加载进 GIS 软件 ogrinfo -so -al southeast_asia_provinces.shp输出里会有一行Layer SRS WKT,看到GEOGCS["WGS 84"...]就是地理坐标系,看到PROJCS[...]就是投影坐标系。这一步别省,我见过太多人把投影坐标当经纬度直接往 Leaflet 里塞,结果地图飞到南极去了。
编码问题同样常见。东南亚地名里有大量非 ASCII 字符,越南语、泰语、高棉语转写成拉丁字母后还带各种变音符号。shapefile 的属性表编码靠 .cpg 文件声明,如果 .cpg 缺失或者写的是错的,打开就是一堆问号。稳妥做法是读的时候显式指定编码:
import geopandas as gpd # 显式指定编码,避免东南亚地名变乱码 provinces = gpd.read_file("southeast_asia_provinces.shp", encoding="utf-8") cities = gpd.read_file("southeast_asia_cities.shp", encoding="utf-8") print(provinces.crs) # 确认坐标系 print(provinces.columns.tolist()) # 看字段名 print(cities.head(3)) # 抽查城市点属性encoding参数是关键,不写的话 GeoPandas 会按系统默认编码猜,Windows 上大概率猜成 GBK,东南亚文字直接报废。crs打印出来确认一下,后面做任何空间操作之前心里有底。
2.3 字段设计:省界和城市点各带什么属性
省界图层的属性表一般包含省名、所属国家、可能的行政区代码。字段名可能是英文缩写,比如PROV_NAME、COUNTRY、ADM_CODE这类。城市驻地图层通常带城市名、所属国家、可能还有人口或行政级别字段。河流和水域图层的属性相对简单,可能只有名称和类型。
字段名不统一是这类资源的通病,有的用大写,有的用驼峰,有的干脆用拼音缩写。读进来之后先columns.tolist()看一眼,后面写代码时按实际字段名来,别照着记忆硬写。如果要做按国家聚合,先确认COUNTRY字段里的值是否规范,有没有前后空格、大小写混用的情况,这些细节不处理,groupby 的时候会多出好几个重复组。
3. 把shp用起来:加载、裁剪、叠加与出图
3.1 用GeoPandas做基础加载与快速预览
数据读进来之后,第一步是快速看一眼空间分布对不对。最省事的办法是直接 plot:
import matplotlib.pyplot as plt # 快速预览四类要素的叠加效果 fig, ax = plt.subplots(figsize=(12, 10)) provinces.plot(ax=ax, facecolor="none", edgecolor="gray", linewidth=0.5) rivers.plot(ax=ax, color="blue", linewidth=0.8) water.plot(ax=ax, color="lightblue", alpha=0.6) cities.plot(ax=ax, color="red", markersize=8) ax.set_title("Southeast Asia Overview") plt.savefig("overview.png", dpi=150, bbox_inches="tight")这段代码的逻辑是分层叠加:省界只画边不填充,避免盖住下面的河流;河流用蓝色线;水域用浅蓝填充加透明度;城市点用红色小圆点。facecolor="none"和alpha这两个参数是出图能不能看的关键,不设的话省界面会把河流全遮住。bbox_inches="tight"去掉多余白边,出图直接能用。
如果图出来发现某个国家缺了一块,或者城市点飘到海里去了,先别怀疑数据错,大概率是坐标系没对齐。用provinces.crs == cities.crs判断一下,不相等就先to_crs()统一。
3.2 按国家或区域裁剪:别每次都被全量数据拖慢
全量东南亚数据做局部分析时,每次加载全部要素纯属浪费。常见做法是先按国家或自定义范围裁一刀:
# 按国家名筛选,比如只看中南半岛某国 target = provinces[provinces["COUNTRY"] == "Vietnam"] # 或者用空间范围裁剪,bbox 格式为 (minx, miny, maxx, maxy) from shapely.geometry import box clip_area = box(100, 10, 110, 25) # 大致覆盖中南半岛东部 clipped = gpd.clip(provinces, clip_area)gpd.clip的好处是它会保留几何完整性,不会把跨边界的面切得七零八落。box的四个参数顺序是(minx, miny, maxx, maxy),别写反了,写反了裁出来是空的,还以为是数据问题。裁剪之后再做渲染或统计,速度会快很多,尤其是河流这种线要素多的时候。
3.3 空间叠加与统计:省界里有多少河流、城市落在哪个省
这份数据真正有用的地方在于能做空间关系分析。比如统计每个省内的河流长度,或者判断每个城市属于哪个省:
# 城市点做空间连接,挂上所属省的信息 cities_with_province = gpd.sjoin(cities, provinces, how="left", predicate="within") # 按省统计城市数量 city_count = cities_with_province.groupby("PROV_NAME").size().reset_index(name="city_num") # 河流按省做叠加,算每个省内的河流总长度 rivers_prov = gpd.overlay(rivers, provinces, how="intersection") rivers_prov["length_km"] = rivers_prov.geometry.length / 1000 # 粗略换算,投影坐标系下更准 river_length = rivers_prov.groupby("PROV_NAME")["length_km"].sum()sjoin的predicate="within"表示点落在面内才算匹配,how="left"保证没有匹配上的城市也保留。overlay做的是几何求交,把河流按省界切开,再按省聚合长度。注意geometry.length在 WGS84 下算出来是度,不是米,要精确算长度得先转到投影坐标系,比如to_crs(epsg=32648)之类。这一步是很多人忽略的,算出来的长度差个几十倍还找不到原因。
3.4 导出与共享:GeoJSON、CSV 和图片怎么选
分析完要给别人用,导出格式得看场景。要保留几何给前端地图用,导 GeoJSON;只要属性表做表格分析,导 CSV;只要出图,导 PNG 或 PDF:
# 导出为 GeoJSON,方便前端或 Web 地图加载 clipped.to_file("output.geojson", driver="GeoJSON") # 只要属性表,去掉几何列导 CSV city_count.to_csv("city_count.csv", index=False) # 出图保存 fig.savefig("final_map.png", dpi=300, bbox_inches="tight")GeoJSON 的坑是文件体积,省界这种面数据全量导出来可能几十兆,前端加载会卡。常见做法是先用simplify()做几何简化,容差根据出图比例尺调,一般 0.01 度左右肉眼看不出来差别,体积能小一半以上。
4. 避坑与排查:坐标系、编码、几何有效性那点事
4.1 现象:地图加载后位置整体偏移几百米到几公里
原因:数据本身是某个地方坐标系或者老版基准,和当前底图的 WGS84 不一致。东南亚部分国家历史数据常用本地基准,直接叠加就会偏。
解决:先确认 .prj 里的基准,用to_crs(epsg=4326)转到 WGS84。如果 .prj 缺失,拿几个已知地标点反推偏移量,或者找同区域的权威数据做配准。别硬调,硬调出来的偏移量换个区域就不对了。
4.2 现象:属性表里东南亚地名全是问号或方块
原因:.cpg 文件缺失或编码声明错误,读取时按系统默认编码解析。
解决:读的时候显式指定encoding="utf-8",如果还是乱码,试"latin-1"或"cp1252"。实在不行用ogrinfo看原始编码,或者用 QGIS 打开时手动选编码。导出时记得同时生成 .cpg 文件,不然下一个用的人还得再踩一遍。
4.3 现象:做 overlay 或 buffer 时报拓扑错误
原因:面数据存在自相交、重复节点、缝隙等几何问题,shapefile 格式本身对几何有效性要求不严,但空间运算会报错。
解决:运算前先做几何修复:
# 修复无效几何,buffer(0) 是常用技巧 provinces["geometry"] = provinces.geometry.buffer(0) # 或者用 make_valid from shapely.validation import make_valid provinces["geometry"] = provinces.geometry.apply(make_valid)buffer(0)是老牌偏方,大部分自相交能修好,但偶尔会把小面修没。make_valid更稳,但版本要求高一些。修完再跑 overlay,报错概率大幅下降。
4.4 现象:城市点明明在陆地上,sjoin 却匹配不到任何省
原因:省界和城市点坐标系不一致,或者省界面有微小缝隙,点正好落在缝里。
解决:先统一 CRS,再做一次buffer(0.0001)给省界加一点点容差,或者用sjoin_nearest找最近的面。别小看这个缝隙,shapefile 转格式时经常产生,肉眼看不出来,空间连接时全暴露。
4.5 现象:河流图层在某些区域断成好几截
原因:原始数据采集时按图幅分块,接边处没做融合,或者河流本身是季节性河道,数据里只记录了有水段。
解决:用linemerge把相邻线段合并,或者按名称字段做 dissolve:
# 按河流名称合并断线 rivers_merged = rivers.dissolve(by="RIVER_NAME")如果名称字段也有缺失,那就只能按空间距离做容差连接,snap到一定阈值内的端点。这个操作要谨慎,阈值设大了会把不该连的河连起来。
5. 进阶技巧:用这份数据做一张能直接汇报的专题图
数据能读、能裁、能算之后,最后一步是出图。汇报用的专题图和自己看的不一样,要素取舍、配色、标注都得讲究。我一般会按这个流程走:先确定地图范围,东南亚全境的话用等距圆柱投影看着舒服,局部区域就切到对应 UTM 分区;然后分层设色,省界按某个指标做分级渲染,河流水域用统一蓝色系,城市点用对比色突出;最后加标注,城市名只标主要驻地,省名按面积或重要性筛选,不然密密麻麻全是字。
具体到代码,分级渲染可以这样写:
# 按城市数量给省界做分级设色 fig, ax = plt.subplots(figsize=(14, 12)) city_count.plot( column="city_num", ax=ax, cmap="YlOrRd", legend=True, edgecolor="gray", linewidth=0.3, legend_kwds={"label": "City Count", "orientation": "horizontal"} ) rivers.plot(ax=ax, color="#4A90D9", linewidth=0.6, alpha=0.7) cities.plot(ax=ax, color="black", markersize=5, marker="o") # 只标注城市数量最多的前十个省 top10 = city_count.nlargest(10, "city_num") for _, row in top10.iterrows(): centroid = row.geometry.centroid ax.annotate(row["PROV_NAME"], xy=(centroid.x, centroid.y), fontsize=8, ha="center", color="darkred") ax.set_axis_off() plt.savefig("report_map.png", dpi=300, bbox_inches="tight")cmap="YlOrRd"是黄到红的渐变,适合表达数量多少。legend_kwds把图例放到底部水平排列,比默认的右侧竖排省空间。标注只取前十个,避免全标导致重叠。set_axis_off()去掉坐标轴,汇报图不需要经纬度刻度。
有一个细节值得单独说:centroid对于形状不规则的省,质心可能落在省外或者海上。稳妥做法是用representative_point(),它保证点在几何内部:
centroid = row.geometry.representative_point()这个坑我在做岛屿省份标注时踩过,质心飘到海里,标注线拉得老长,图上一团乱。从那以后我每次做面标注都强制走一遍representative_point(),再也没出过洋相。
出图之后如果还要做交互式地图,把 GeoJSON 导出来丢给 Leaflet 或者 Mapbox,省界做 choropleth,河流水域做 overlay,城市点做 marker,这套数据的分层结构刚好对得上。唯一要注意的是 Web 端加载前先做几何简化,不然首屏加载能等到睡着。
希望这份拆解帮到你,拿到数据先跑一遍ogrinfo和plot,心里有底再往下做,比直接闷头写分析代码省时间得多。
本文还有配套的精品资源,点击获取