简介:一款基于1:400万比例尺的中国地图地理数据产品,重点呈现地市级以上居民地信息,并完整包含中国南海区域。在此比例尺下,图上1单位约相当于实地400万单位,适合宏观区域概览,仍能清晰展现地级市与县级行政中心、主要道路、水系等基本要素,便于城市布局对比与区域结构分析。地图数据适用于地理科研、教学设计、城市规划、宏观导航及南海议题研讨,可帮助用户直观把握行政区划与海域空间关系。压缩包约2.13MB,包含一个地图数据主文件,内容简洁紧凑,可供主流GIS平台加载查看,也可导出或叠加其他专题图层使用。已有158人学习下载。通过该地图,读者既能获得全国地市级以上居民地的整体框架,也能重点识别南海主要岛屿、礁石、航道及海域标注,为理解海洋权益范围、生态环境和资源分布提供基础底图支持。面向研究者、政策制定者与教育工作者,是一份兼具实用性和参考价值的地理数据。
1. 从一张“全国地市图”说起:china_basic_map 带南海的数据底图
做 GIS 的人应该都有过这种经历:项目需要一张全国地市级以上的宏观底图,网上下载的“全国行政区划图”要么只画到大陆轮廓,要么把小比例尺南海诸岛糊在角落,出图比例完全不受控。我拿到china_basic_map这份数据时,第一反应是检查两件事:地市级居民地要素是否齐全,南海范围是否被完整纳入。这份资源以 1:400 万比例尺描述了全国地市级及以上居民地,文件名为china_basic_map,关键点在于“带南海”,也就是说图幅范围、要素组织都考虑了南海区域,省去了手工拼接和比例尺对不齐的麻烦。适合做区域规划、人口分布趋势分析、环境区划专题图和教学底图,下面按实际处理流程拆开讲。
2. 解析china_basic_map的文件格式与1:400万坐标基准
拿到一份 GIS 数据,先不要急着砸进 ArcMap,搞清楚格式、比例尺和坐标基准,后面才不会被莫名其妙的空间位置偏差耽误时间。这一章从 1:400 万比例尺的实际含义、常见文件格式的识别方式、以及坐标系对南海图幅的影响三个角度展开。
2.1 1:400万比例尺意味着什么
比例尺的本质是图上距离与实际距离的比值。1:400万即图上 1 厘米代表实际 40 千米,1 毫米代表实际 4 千米。在这个比例尺下,地级市行政范围大概一两个平方厘米,居民点要素可以表示为点或小面,而乡镇级边界已经无法清楚区分。所以这份数据适合做宏观对比和制图综合,不适合街道级分析。下面的表给出了不同比例尺的适用场景参考:
| 比例尺 | 图上1cm代表实地 | 典型用途 |
|---|---|---|
| 1:100万 | 10 km | 省级行政区划、资源分布图 |
| 1:400万 | 40 km | 全国性专题图、区域规划底图 |
| 1:1000万 | 100 km | 亚洲或全球范围示意 |
| 1:2500万 | 250 km | 世界地图、宏观区位图 |
了解比例尺之后还要想一个问题:1:400万的数据从哪里来。最常见的来源是国家基础地理数据中的市县级面边界经聚合抽稀得到的成果,要素字段通常保留了地市名称、行政代码和省份信息,而几何精度被刻意降低到一个适合视觉表达的容差。因此用它做分析时要意识到,边界线与真实地界可能有一到两个像素的偏移,这种偏移不影响全国尺度的趋势判断,但如果叠加高精度遥感影像,就能明显看出误差,需要预先把结果说清楚。
2.2 文件格式的判断与驱动匹配
文件列表里只有一个china_basic_map,没有扩展名,这在实际下载资源里很常见。GIS 数据格式里,GeoJSON、KMZ、GeoTIFF 都是单文件,而 Shapefile 则要求.shp、.shx、.dbf、.prj等多个文件并存。如果只拿到一个文件,第一步就是用工具自动探测它到底是哪一种,而不是手工改扩展名。fiona是 GDAL 的 Python 封装,可以直接读取文件头和图层信息:
import fiona # 不指定扩展名,让fiona尝试自动识别文件驱动 path = "china_basic_map" try: with fiona.open(path) as src: print("驱动:", src.driver) print("图层数量:", len(src)) print("坐标系:", src.crs) except Exception as e: print("自动识别失败:", e) # 常见情况是没有识别出扩展名,尝试常见的后缀 for suffix in [".shp", ".geojson", ".json", ".kmz", ".tif"]: try: with fiona.open(path + suffix) as src: print(suffix, "->", src.driver, src.crs, len(src)) except Exception: pass这段代码先按无后缀方式打开,让 GDAL 通过文件内容判断驱动;失败则依次尝试.shp、.geojson、.kmz、.tif等常用后缀。src.driver返回如ESRI Shapefile或GeoJSON这样的驱动名,src.crs返回坐标参考系,len(src)是要素总数。需要注意,如果探测出 Shapefile,但目录里缺少.prj文件,src.crs会返回空值,这时需要从元数据或原发布说明里补坐标信息,不然后续所有投影计算都会出错。不同格式存储特点可以看下面这张表:
| 格式 | 单文件? | 坐标系存储位置 | 适用工具 |
|---|---|---|---|
| Shapefile | 否 | .prj文件 | QGIS、ArcGIS、GeoPandas |
| GeoJSON | 是 | 顶层crs属性(不强制) | Web地图、PostGIS、GeoPandas |
| KML/KMZ | 是 | 文档内 GroundOverlay 或 CRS 标签 | Google Earth、浏览器 |
| GeoTIFF | 是 | 文件头地理标签 | 栅格分析、遥感处理 |
fiona.open的自动识别在多数本地文件场景下够用,但如果拿到的是一个没有合法扩展名的自定义压缩包,最好先解压看目录结构。我见过不少资源发布时把.shp的三个附属文件打包进一个同名子目录,真正读取路径要指向子目录中的.shp文件,而不是外层压缩包名。
2.3 坐标系基准与带南海的范围约定
全国 1:400 万数据常见的坐标基准有两种:WGS84 和 CGCS2000,两者在平面位置上的差异通常小于 1 米,在 1:400 万比例尺下视觉上完全看不出来。但投影方式差别就大了,经度纬度直接叠加到平面图上,必须明确是用地理坐标还是投影坐标。一般全国图习惯用 Albers 等积圆锥投影或 Lambert 等角圆锥投影,前者适合面积统计,后者适合方向、形状保持。如果你只是出图看分布,选 WGS84 地理坐标直接绘图也没问题;一旦要做面积汇总、距离测量,就一定要转成投影坐标。
import geopandas as gpd gdf = gpd.read_file("china_basic_map") print("原始CRS:", gdf.crs) # 如果原始CRS为空,先假定WGS84再转Albers if gdf.crs is None: gdf = gdf.set_crs("EPSG:4326") # 转换到Albers等积圆锥投影,适合中国全境面积分析 gdf_albers = gdf.to_crs("+proj=aea +lat_1=25 +lat_2=47 +lat_0=0 +lon_0=105 +datum=WGS84") print("转换后CRS:", gdf_albers.crs)参数说明:set_crs用于给没有坐标信息的图层指定坐标,EPSG:4326是 WGS84 经纬度坐标;to_crs将要素转换到目标投影,目标字符串里proj=aea表示 Albers 等积圆锥,lat_1=25和lat_2=47是两条标准纬线,lon_0=105是中央经线,这样整个中国区域包括南海都能落在较小的变形范围内。带南海的数据处理时要特别注意一个细节:to_crs会统一转换所有要素,所以只要主图里有南海诸岛,转换后它们仍然和大陆保持正确的相对位置。但如果你是自己手动拼接的南海附图,就必须主图和附图使用同一个投影参数,否则会出现南海附图被拉伸或偏出图框的问题。
3. 用GeoPandas读取并整理地市级以上居民地数据
格式和坐标搞清之后,就可以把数据真正读进来做属性整理了。GeoPandas 的 API 习惯和 Pandas 很像,适合快速翻看要素属性、过滤筛选以及做空间叠加。这里按实际流程讲一遍:先读数据,再字段清洗,再做空间筛选。
3.1 读取并检查数据项
GeoPandas 的read_file可以直接读取 Shapefile、GeoJSON、GeoPackage 这些常见格式,不指定扩展名时也能借助底层 GDAL 自动识别。读进来后,第一件事是查看crs、columns和几何类型,确认居民地是点还是面数据。
import geopandas as gpd gdf = gpd.read_file("china_basic_map.shp") print("CRS:", gdf.crs) print("几何类型: ", gdf.geom_type.unique()) print("字段列表:", gdf.columns.tolist()) print(gdf.head(10).to_string())这段输出能帮你在几十秒内判断数据是否完整。gdf.geom_type.unique()返回['Point']、['Polygon']或['Point', 'Polygon']等组合。如果居民地是点,但你又需要地市行政面边界做填充,则该文件里通常还包含面要素,只是没有一起读出来;这时就要用层名称区分。字段列表里如果看到NAME、GB、PROVINCE这类英文缩写,大概率是国家基础地理数据风格;中文名如地市名称、行政代码则可能是后期整理的成果。建议先把多余的字段删掉,只留名称、省份、行政代码三个核心字段,避免后续 join 时类型冲突。
3.2 地市级以上居民地的属性筛选与字段整理
原始数据名称不规范是最常见的问题。比如有的记录叫“省直辖县级行政单位”,有的叫“地级市”,还有开发区、林区等特殊区划。如果数据源声称“地市级以上居民地”,那属性表里一般会有分类字段。假设字段名为type,筛选逻辑可以这样写:
# 只保留地级市、直辖市、省会/首府类型 keep_type = ['地级市', '直辖市', '省会', '首府', '自治州'] filtered = gdf[gdf['type'].astype(str).isin(keep_type)].copy() # 统一显示名称,去除括号备注 filtered['display_name'] = filtered['name'].astype(str).str.replace(r'(.*)', '', regex=True) # 用省份和名称拼接唯一标识,避免跨省重名 filtered['label'] = filtered['province'] + '-' + filtered['display_name'] print(filtered[['label', 'type', 'geometry']].head())代码逻辑要注意三点:第一,astype(str).isin(keep_type)是防御性写法,因为字段里可能出现空值或类型不一致;第二,正则替换去掉了名称里的“(含xx区)”之类备注,让出图标签更干净;第三,用“省份-名称”做唯一标识,可以规避“朝阳区”这类跨省同名问题。筛选完成后,检查一下残留的要素数量,如果比国家实际地级行政单位数量少,很可能是type字段有缺失,这时可以改用行政代码前缀筛选,地级市代码通常以 4 开头且末两位非 00,但具体要看数据源的编码规则。下面的字段表可以对照查看:
| 字段名 | 示例 | 用途 |
|---|---|---|
| name | 杭州市 | 基本名称 |
| type | 地级市 | 行政区划类型 |
| province | 浙江省 | 所属省份 |
| adcode | 330100 | 行政区划代码 |
| population | 8500000 | 人口信息(可选) |
3.3 叠加分析:与省级界线、南海范围线相交
在专题制图中,经常需要把南海区域的边界范围线与地市级居民地点合并展示。如果带南海的数据把南海界线单独做成了一个面或线图层,就要做空间叠加,把落在南海界线内的居民地单独提取出来。
# 假设sea是一个包含南海范围线的面图层 sea = gpd.read_file("sea_boundary.geojson") # 统一坐标系 sea = sea.to_crs(gdf.crs) # 方式一:直接把数据裁剪到南海界线多边形范围内 south_china_cities = gpd.overlay(gdf, sea, how="intersection") # 方式二:只筛选中心点落在南海范围内的要素 sea_union = sea.geometry.unary_union south_china_cities2 = gdf[gdf.geometry.centroid.within(sea_union)] print(south_china_cities.head())gpd.overlay的how="intersection"会返回两个图层几何相交的部分,适合面要素;如果居民地是点,用centroid.within(sea_union)更轻量,它会把多边形的unary_union先合并成一个组合几何,再判断每个点的中心是否落在其中。要注意,overlay对几何拓扑要求较高,如果sea图层存在自相交或缝隙,结果可能产生碎片要素。遇到这种情况,先用sea.geometry.buffer(0)修复无效几何,再继续叠加。
4. 南海诸岛的制图实现:从数据范围到分图框出图
带南海的全国底图,出图效果和普通地图最大的不同在于南海区域如何呈现。这一章讲清楚南海数据是怎么组织的,以及两种主流出图方案:直接用一个大图框包含全国和南海,或者用“主图+南海附图”的拼接方式。
4.1 南海诸岛在数据中的组织方式
打开china_basic_map后,建议先检索一下图层里和南海相关的要素。名称可能带有“南海”“诸岛”“海域”等字样,也可能是独立的几何类型。用下面的代码快速定位:
import geopandas as gpd gdf = gpd.read_file("china_basic_map") # 检索名称中含南海/诸岛的要素 sea_like = gdf[gdf['name'].astype(str).str.contains('南海|诸岛|海域', na=False)] print(sea_like[['name', 'geometry']].head()) print("要素数量:", len(sea_like))如果查询结果为空,不代表数据不支持南海,而是南海范围可能作为一条线附在国界要素里,例如boundary字段里多出了“海域界线”记录。这时可以按几何范围截取:南海区域大致对应经度 105°E 到 124°E、纬度 2°N 到 25°N,把和该边界框相交的要素全部保留即可。需要留意的是,有些资源把南海诸岛画成独立点集,便于在小比例尺下逐个标注岛礁名称;而行政边界和九段线则属于面或线图层,两者不能混在同一图层里做样式控制。制图前最好把南海范围拆成单独的变量,便于附图配置。
4.2 matplotlib分图框拼接主图与南海附图
用程序出图时,最可控的方案是使用 matplotlib 的GridSpec或多个Axes。主图放置全国主要区域,附图放在右下角或左下角。由于南海附图通常需要放到主图内的空白处,推荐用fig.add_axes指定绝对位置,而不是默认的网格平铺。下面的代码实现了一个简洁的主图+南海附图拼接:
import matplotlib.pyplot as plt import geopandas as gpd gdf = gpd.read_file("china_basic_map.shp") fig = plt.figure(figsize=(10, 12), dpi=100) # 主图坐标系,覆盖中国大陆主要范围,实际范围按数据调整 ax_main = fig.add_axes([0.02, 0.08, 0.92, 0.88]) # 绘制所有地市级以上居民地,面和点分开控制样式 if "Polygon" in gdf.geom_type.unique(): gdf.plot(ax=ax_main, facecolor="#e8e8e8", edgecolor="#444444", linewidth=0.4) else: gdf.plot(ax=ax_main, color="#1f6feb", markersize=3) ax_main.set_xlim([73, 135]) ax_main.set_ylim([18, 54]) ax_main.set_title("全国地市级以上居民地分布", fontsize=14) # 创建南海附图,放在主图右下角空白位置 ax_sea = fig.add_axes([0.68, 0.16, 0.28, 0.28]) gdf.plot(ax=ax_sea, facecolor="#e8e8e8", edgecolor="#444444", linewidth=0.3) ax_sea.set_xlim([105, 124]) ax_sea.set_ylim([2, 25]) ax_sea.set_title("南海诸岛", fontsize=10) # 关闭子图刻度,避免影响整饰 ax_sea.set_xticks([]) ax_sea.set_yticks([]) ax_main.set_xticks([]) ax_main.set_yticks([]) plt.show()这里的核心参数是add_axes的四元组[left, bottom, width, height],使用归一化单位。主图范围[0.02, 0.08, 0.92, 0.88]占满画布;附图[0.68, 0.16, 0.28, 0.28]定位在主图右下角,既不遮挡海岸线,又能完整显示南海诸岛。set_xlim和set_ylim直接决定了图幅范围,主图如果希望大陆部分保留更多留白,可以把 x 范围设为 73 到 135,y 范围设为 18 到 54;如果数据有完整南海边界,并且不允许“断离”,则主图应该直接包含南海坐标范围。两种方式的取舍要看数据说明,如果资源本身已经把南海作为主图的一部分,则不需要附图。一般“带南海”的数据更推荐一个图框全覆盖,这样在投影转换、比例尺标注上不会出现主图和附图比率不一致的尴尬。
4.3 QGIS打印布局中的多数据框设置
如果是非程序员或希望快速出一个工程文件,QGIS 的打印布局(Print Layout)比 matplotlib 更直观。操作步骤可以这样拆:
- 在图层列表里先加载主数据和南海数据,确认两个图层的 CRS 一致。
- 点击菜单“项目 → 新建打印布局”,添加一个地图组件作为主视图。
- 选中地图组件,在“项目属性 → 图层”里设置范围和比例尺,比如设置为 1:400万。
- 再添加第二个地图组件,将范围手动拖动到南海区域(105°E到124°E,2°N到25°N)。
- 在“项目属性 → 图层”中关闭不需要的图层,让第二个组件只显示南海相关要素。
两种方式中,QGIS 的优点是出图符号化扩展丰富,而且比例尺、图例、指北针都能自动同步。但有一个容易忽略的地方:附图的比例尺如果和主图不同,一定要在附图下方标注“不同比例尺”,否则看图者会误以为附图与主图等比例。
5. 动手收尾:几何简化、坐标系校验与出图检查
到这一步数据已经能画出来了,但发布前还有两个细节值得花十分钟处理:几何简化和尺度校验。这两步看着不起眼,却能避免很大的返工。
5.1 用shapely简化高精度边界
原始数据如果是从 1:100 万或更高精度缩编而来,边界线会有大量细碎节点,放在 1:400 万比例尺下会显得很毛糙。用shapely的simplify方法可以按容差抽稀,把不必要的折点去掉,保持视觉整洁。
# 按地理坐标容差0.01度约相当于1公里,适合1:400万 gdf_simplified = gdf.copy() gdf_simplified["geometry"] = ( gdf_simplified.geometry.simplify(tolerance=0.01, preserve_topology=True) ) # 检查简化前后要素数量 print("原要素数:", len(gdf)) print("简化后要素数:", len(gdf_simplified))tolerance的单位与当前坐标系一致。如果数据是经纬度坐标,0.01度约等于 1 公里,正好匹配 1:400 万比例尺下的视觉精度;如果数据已经是投影坐标,tolerance的单位是米,可以设为 1000。preserve_topology=True可以阻止简化过程中出现自相交或相邻面重叠,这个参数非常关键,越小的容差不一定会让图更快,但一定能避免拓扑破碎。
5.2 坐标转换与比例尺校验
出图前还要做一次比例尺校验。方法很简单:在主图里选两个已知实地距离的地级市,用测量工具量一下图上距离,再换算实际比例尺。例如从北京到上海直线距离约 1070 公里,在 1:400 万比例尺下图面上应该是 2.675 厘米,如果量出来的数差得太多,说明坐标系或坐标系转换阶段出了问题。
from shapely.geometry import LineString # 假设两个点已经投影到Albers坐标,单位是米 city_a = gdf.loc[gdf["name"] == "北京", "geometry"].values[0] city_b = gdf.loc[gdf["name"] == "上海", "geometry"].values[0] distance_m = city_a.distance(city_b) # 如果投影坐标用米,则1:400万下的图上距离(cm) = 实际距离(cm) / 4e6 cm_on_map = distance_m * 100 / 4_000_000 print("图上距离估算值(cm):", round(cm_on_map, 3))city_a.distance(city_b)返回的是投影坐标平面上的直线距离,单位是米。这个值只用于粗略校验,它忽略了球面曲率,但对全国尺度来说已经够用。如果算出来的图上距离远大于实际纸张允许值,应检查to_crs的投影参数是否将经纬度错误解释为米。
5.3 出图前检查项
最后可以按下面这个清单快速过一遍数据,防止小问题出差错:
| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 坐标参考 | print(gdf.crs) | 不为空,且所有图层一致 |
| 南海范围 | 查看total_bounds | 包含经度 105°~124°,纬度 2°~25° |
| 几何有效性 | gdf.is_valid.all() | 返回True |
| 行政代码唯一 | gdf["adcode"].is_unique | 无重复 |
| 名称空值 | gdf["name"].isna().sum() | 0 |
如果is_valid.all()返回False,执行一行修复代码即可:gdf.geometry = gdf.geometry.buffer(0)。这套流程做完,导出的图无论是丢进 PowerPoint 还是作为论文插图,比例尺和行政边界都不会被退回重改。
本文还有配套的精品资源,点击获取