简介:杭州市2020年POI数据集是一份面向GIS开发、城市规划与数据分析人员的空间数据资源,包含30米分辨率DEM、行政区划边界,以及覆盖生活服务、餐饮、购物、住宿、风景名胜等多类业态的兴趣点数据,可直接用于空间分析、地图制图或选址研究。整个压缩包共120个文件,以shp及配套的dbf、prj、shx等矢量文件为主,另有13个xlsx表格和部分tif高程数据,包体约49.62MB,结构清晰便于分层使用。已有868人学习下载,适合需要快速获取杭州市基础地理数据做实验或项目验证的读者。数据同时提供shp和excel两种格式,兼顾GIS软件与办公场景;DEM与行政区划可辅助地块解读与三维展示,POI分类较细,能支撑可达性分析、商圈识别等进阶应用。数据标注为非涉密,使用门槛低,可作为日常研究与教学练习的可靠底图。
1. 拿到一个市域级 POI 数据集,先别急着打开 ArcGIS
做城市数据分析和 GIS 开发的读者,拿到「杭州市 2020 年 POI 数据集,包含 30M 分辨率 DEM、行政区划、shp 格式 POI、excel 格式 POI 数据.7z」这个压缩包时,第一反应往往是直接双击解压,把 shp 拖进地图里看。但这类数据集有一个共性:文件完整不代表数据可用,坐标系、编码、字段语义才是真正的门槛。这篇文章会把这份数据集拆成四块来讲——POI 的两种格式、DEM 与行政区划的用途、7z 压缩的处理方式,以及从解压到出图的全流程操作。适合需要把外部 GIS 数据接进自己项目的工程师,也适合做城市分析但被数据格式卡住的新手。
2. POI 数据集里的格式选型:shp 和 excel 各自承担什么
2.1 先搞清楚 POI 在 GIS 语境里的含义
POI全称 Point of Interest,即兴趣点。它代表地图上一个具体的、有业务意义的点,比如餐饮门店、加油站、学校、医院。和路网、建筑物轮廓这类线面数据不同,POI 的核心是「点在哪儿 + 这个点是什么」。注意,这里的 POI 和 Java 生态里的 Apache POI API 没有任何关系——那个是操作 Office 文档的库,经常被拿来读写 Excel,热搜里提到的apache poi <= 4.1.0 xssfexporttoxml xxe漏洞属于办公文档解析领域,和本数据集完全不沾边。
一个市域级的 POI 数据集,通常包含几十万到上百万条记录。杭州作为大城市,2020 年的 POI 数量至少在几十万量级。这样的数据量用 shp 格式存储是合适的,因为 shapefile 本身就是为矢量点数据设计的存储格式。
2.2 shp 格式:GIS 分析的主战场
shapefile 虽然叫「一个」文件,实际落地是多个文件的集合,至少包含.shp(几何)、.shx(索引)、.dbf(属性表),还可能附带.prj(坐标系)、.cpg(字符编码)。这种多文件结构是新手最容易踩坑的地方——只拷贝.shp一个文件到别处,打开就会报错。
用geopandas读取这种 POI 数据是常见做法,下面这段代码能在不打开桌面软件的情况下快速了解数据结构:
import geopandas as gpd # 读取杭州市 POI 的 shp 文件,注意路径里的中文编码问题 poi = gpd.read_file("杭州市2020年POI数据/hangzhou_poi.shp", encoding="utf-8") print(poi.shape) # (行数, 列数) print(poi.columns) # 字段名列表 print(poi.crs) # 坐标系信息 print(poi.head()) # 前5行读取时encoding参数经常要试:有的数据源是utf-8,有的是gbk。试错的方法很简单——读出来看中文字段名是否乱码,乱码就换编码。.dbf文件对字段名长度有限制,超过 10 个字符会被截断,所以字段名往往是简写,需要通过字段注释或数据字典来还原语义。
2.3 excel 格式:给业务人员用的接口
同一个 POI 数据集为什么要再提供一份 excel 格式?因为不是所有协作方都会用 GIS 工具。业务部门、运营团队用 Excel 打开.xlsx直接筛选、做透视表,比让他们装 QGIS 现实得多。
但这个字段通常在 shp 里是否完整一致,需要验证。常见的做法是读两个文件做行数和 ID 的交叉核对:
import pandas as pd poi_excel = pd.read_excel("杭州市2020年POI数据/hangzhou_poi.xlsx") poi_shp = gpd.read_file("杭州市2020年POI数据/hangzhou_poi.shp", encoding="utf-8") # 比较 ID 唯一性,判断两份数据是否能互相印证 print(poi_excel["poi_id"].is_unique) print(poi_shp["poi_id"].is_unique) print(set(poi_excel["poi_id"]) == set(poi_shp["poi_id"]))Excel 格式的固有问题是经纬度列被当作文本存储,或者小数位被截断。检查办法是看数据类型,dtype如果是object而不是float64,就需要用pd.to_numeric做转换。另外 Excel 另一个常见坑是行列数超过 1048576 行时的截断风险——市域 POI 数据通常不至于到这个量级,但如果是全国数据就要留意。
2.4 DEM 与行政区划在数据集里的角色
DEM(Digital Elevation Model)是数字高程模型,30M 分辨率意味着每个像元代表地面 30 米 × 30 米的区域。这个精度在城市级别恰好够用:能做坡度、坡向分析,能判断 POI 所在位置的地形起伏,也能用来做可视域分析。30M 相比ALOS 12.5米那种精细数据要粗一些,但城市地形相对平缓,做宏观分析时差别不大。
行政区划数据在本数据集里是边界约束。做 POI 统计时,把点叠到行政区面里,按区县聚合,这是最常见的用法。用空间连接实现:
import geopandas as gpd district = gpd.read_file("杭州市2020年POI数据/hangzhou_district.shp", encoding="utf-8") poi = gpd.read_file("杭州市2020年POI数据/hangzhou_poi.shp", encoding="utf-8") # 空间连接:把每个 POI 归属到所在行政区 join = gpd.sjoin(poi, district, how="left", op="within") # 按区县统计 POI 数量 count_by_district = join.groupby("district_name").size().reset_index(name="poi_count") print(count_by_district.sort_values("poi_count", ascending=False))sjoin的op="within"是点面连接最常用的空间谓词,表示点必须在面内部。边界上的点如果出现未匹配情况,通常是坐标系不一致导致的,这在第 3 章统一坐标系后会解决。
3. 7z 压缩包的解压与数据落地:从命令行到 Python
3.1 用 7z 命令行核对压缩包完整性
.7z格式的压缩率比 zip 高,一个包含 shp 多文件 + tif 栅格 + xlsx 的数据集,压成 7z 能省不少空间。但 7z 在部分系统上不是默认支持格式,所以拿到压缩包第一步是确认工具可用。Windows 上安装 7-Zip 后,命令行工具是7z.exe;Linux 上通常需要自己安装p7zip或7zip包。
先不要急着解压,先列出压缩包内容:
7z l "杭州市2020年POI数据集.7z"l参数是 list 的缩写,只列出内容不解压。这一步能看到压缩包内是否有分卷、目录结构是否符合预期。一个 7z 文件内部可能包含多个文件,确认文件结构后,再执行解压:
7z x "杭州市2020年POI数据集.7z" -o./data -p参数说明:x表示解压并保留目录结构;-o指定输出目录,注意-o后面没有空格;-p后接密码,如果压缩包没加密就直接省略。部分数据源会加密压缩包,密码通常写在下载说明里,7z x会在没有密码时交互式提示输入。
解压完成后检查文件数量是否和l列出的一致,尤其注意.shp的同名伴随文件(.shx、.dbf、.prj、.cpg)是否齐全。缺.prj文件是最麻烦的,意味着坐标系信息丢失,后面要用坐标值反推。
3.2 Python 侧处理 7z 文件
如果整个工作流都在 Python 里,不想为解压切到命令行,可以用py7zr库。它在 Linux、Windows、macOS 上都能用,接口也很简洁:
import py7zr # 打开压缩包并解压到指定目录 with py7zr.SevenZipFile("杭州市2020年POI数据集.7z", mode="r") as archive: archive.extractall(path="./data") # 打印压缩包内文件列表 print(archive.getnames())getnames()返回的是压缩包内部所有文件路径的列表,可以在解压前先打印出来看结构。如果文件很多,可以先只解压需要的后缀,比如只提取.shp相关文件,再按需解压 DEM 的.tif。但我不建议这样分次解压——shp 和 dbf 是配套的,少一个都打不开。
3.3 统一坐标系:把 POI 和 DEM 摆到同一张桌上
解压完成后,最关键的检查是坐标系。POI 数据最常见的坐标系设置是 WGS84(EPSG:4326)或 GCJ02(火星坐标系),而 DEM 栅格一般用投影坐标系,比如 UTM 或高斯-克吕格。杭州区域常用的投影带是 UTM 50N 或 CGCS2000 3 度带。
先分别查看四个数据源的坐标系:
import geopandas as gpd import rasterio poi = gpd.read_file("data/hangzhou_poi.shp", encoding="utf-8") district = gpd.read_file("data/hangzhou_district.shp", encoding="utf-8") # 查看矢量数据的坐标系 print("POI CRS:", poi.crs) print("District CRS:", district.crs) # 查看 DEM 栅格信息 with rasterio.open("data/dem30m.tif") as dem: print("DEM CRS:", dem.crs) print("DEM bounds:", dem.bounds) print("DEM resolution:", dem.res) print("DEM nodata:", dem.nodata)rasterio.res输出的是像元尺寸,如果显示(30.0, 30.0)或类似数值,说明 DEM 保持原始分辨率。但如果打印结果是(0.0002777778, 0.0002777778)这种度数单位,说明这个 DEM 被重投影到了地理坐标系下,这时要谨慎做坡度计算。
如果 POI 和行政区划坐标系不一致,用to_crs统一:
# 假设行政区划是投影坐标系,把 POI 转过去 poi_projected = poi.to_crs(district.crs)选择以行政区划的坐标系为基准,是因为后续空间连接大多以面为容器,保持面的坐标系不变做基础。如果 DEM 需要与矢量叠加,用rasterio.reproject重采样到统一 CRS,重采样方法推荐Resampling.bilinear。
3.4 数据质量初检:空几何、重复点、越界点
坐标系统一后再做一轮质量检查。常见问题包括:空几何(geometry 为 None)、重复经纬度的点、明显超出杭州市范围的坐标。这类问题在数据生产阶段几乎是必然存在的,下面这段脚本能一次性定位问题点:
import geopandas as gpd import numpy as np poi = gpd.read_file("data/hangzhou_poi.shp", encoding="utf-8") # 1. 空几何检查 print("空几何数量:", poi.geometry.isna().sum()) # 2. 重复点检查:基于经纬度列去重 poi["lon_lat"] = poi["lon"].astype(str) + "_" + poi["lat"].astype(str) dup_mask = poi["lon_lat"].duplicated(keep=False) print("重复点数量:", dup_mask.sum()) # 3. 边界范围检查:杭州大致在经度118.3~121.0,纬度29.0~30.8之间 bounds_mask = ( (poi["lon"] > 121.5) | (poi["lon"] < 118.0) | (poi["lat"] > 30.9) | (poi["lat"] < 29.0) ) print("疑似越界点数量:", bounds_mask.sum()) print(poi.loc[bounds_mask, ["poi_name", "lon", "lat"]].head(10))越界点的处理策略取决于用途:如果做全量统计,建议过滤掉;如果做空间分布展示,可以保留并检查是否是飞地数据(比如杭州在某个区域有实际管辖的飞地)。 以及还有坐标在市区范围但被错误标记了经纬度的情况,比如经纬度互换、小数点错位,这类错误靠范围过滤查不出来,需要抽检人工确认。
4. 用 DEM 和 POI 做组合分析:从聚合统计到坡度计算
4.1 按行政区划做 POI 密度统计
把 POI 数据落到行政区划上做统计,是城市分析里最常用的操作。第 2 章的sjoin只能给出数量,实际项目里往往要计算密度——POI 数量除以行政区面积。因为杭州市各区面积差异很大,靠绝对数量比较强弱会失真。密度计算的单位一般是「个/平方公里」,面积需要先将面数据投影到等面积坐标系:
import geopandas as gpd district = gpd.read_file("data/hangzhou_district.shp", encoding="utf-8") poi = gpd.read_file("data/hangzhou_poi.shp", encoding="utf-8") # 投影到适合面积计算的等积投影:Albers 等积投影适合中国中部 district_proj = district.to_crs("EPSG:9822") district_proj["area_km2"] = district_proj.geometry.area / 1e6 # POI 也投影到相同坐标系再做空间连接 poi_proj = poi.to_crs("EPSG:9822") join = gpd.sjoin(poi_proj, district_proj, how="left", op="within") density = ( join.groupby("district_name") .size() .reset_index(name="poi_count") ) density = density.merge( district_proj[["district_name", "area_km2"]], on="district_name", how="left" ) density["poi_density"] = density["poi_count"] / density["area_km2"] print(density.sort_values("poi_density", ascending=False).head(10))这段代码里有两个关键点。第一,使用EPSG:9822等积投影,确保面积计算不因投影变形产生较大误差,南北跨度大的城市尤其明显。第二,groupby之后重新 merge 面积列,因为聚合操作会丢掉非分组字段。 这里有一个容易忽略的边界问题:杭州西湖区与西湖水面重叠的区域属于西湖风景区管理处管辖,边界归属和实际管理方不同。如果分析目的是商业选址,这种边界语义差异需要考虑,必要时用缓冲区分析做补充。
4.2 30M DEM 的坡度计算与坡向提取
POI 数据是点,DEM 是连续表面,两者结合最常见的场景是分析地物所在位置的坡度。比如判断全杭州的餐饮 POI 是否大多分布在缓坡区域,这是个有意思的统计题。坡度计算可以直接用gdaldem命令行工具:
# slope 计算,结果以度为单位输出 gdaldem slope data/dem30m.tif data/slope.tif -s 1.0 -p # hillshade 山体阴影,用于可视化 gdaldem hillshade data/dem30m.tif data/hillshade.tif -z 2.0 -az 315.0 -alt 45.0参数说明:-s是垂直比尺因子,单位与水平方向相同时设为 1.0,一般 30M 数据不用改;-p表示输出坡度的单位为度(percent 不用此参数);-z是高度夸大系数,可视化时我更常用 1.0,真实展示地形效果时可以放大;-az是光源方位角(0-360),315 度是地图学上的默认西北光照;-alt是光源高度角,45 度适中。
如果项目代码在 Python 环境中不希望调用外部命令,用rasterio也能算,需要自己写梯度公式:
import rasterio import numpy as np from rasterio.transform import Affine with rasterio.open("data/dem30m.tif") as src: dem = src.read(1).astype("float64") transform = src.transform nodata = src.nodata # 将 nodata 置为 NaN 防止参与梯度计算 dem[dem == nodata] = np.nan # 计算 x, y 方向的梯度 dx, dy = np.gradient(dem) slope_rad = np.arctan(np.sqrt(dx**2 + dy**2)) slope_deg = np.degrees(slope_rad) # 输出统计信息 print("坡度最大值:", np.nanmax(slope_deg)) print("坡度平均值:", np.nanmean(slope_deg))这里的np.gradient用的是中心差分,对 30M 分辨率的 DEM 足够。注意np.gradient计算时按照行列间距为 1 像素来计算,如果要做物理单位的精确计算需要传入transform.a(x 方向像元大小)作为间距参数。上面代码简化了这一点,在大多数城市宏观分析中影响不大。真正影响大的反而是填洼——原始 DEM 里可能有错误的负值凹坑,做水文分析之前必须用gdaldem filldepressions处理。
4.3 提取 POI 所在位置的高程与坡度值
把 DEM 的连续值提取到 POI 点上,是点栅格叠加的典型操作。比如分析商圈 POI 的海拔分布,或者做洪涝风险评估时重点判断低洼处 POI 的密度。提取做法是rasterio.sample:
import geopandas as gpd import rasterio poi = gpd.read_file("data/hangzhou_poi.shp", encoding="utf-8") # 确保 POI 坐标与 DEM 的坐标系一致 dem_crs = "EPSG:4326" poi_wgs84 = poi.to_crs(dem_crs) # 获取经纬度坐标对 coords = [(x, y) for x, y in zip(poi_wgs84.geometry.x, poi_wgs84.geometry.y)] with rasterio.open("data/dem30m.tif") as dem: # sample 返回生成器,逐个提取像元值 elevs = [val[0] for val in dem.sample(coords)] # 注意 sample 不会自动处理 nodata,需要自己识别 poi_wgs84["elevation"] = elevs # 把 nodata 值替换为 NaN poi_wgs84["elevation"] = poi_wgs84["elevation"].replace(dem.nodata, np.nan) print(poi_wgs84[["poi_name", "elevation"]].head(20))这个操作的坑在于sample遇到 nodata 时会直接返回原始值(比如 -9999),不会自动变成 NaN,所以上面手动替换了一次。如果 DEM 的范围没有完全覆盖 POI,比如边缘的点落在 DEM 文件边界外,sample会返回 nodata 值而不是报错,这也是用过滤兜底的原因。通常在业务分析中,我们会额外标记poi 是否在 dem 覆盖范围内,避免把这些点当成海拔 0 参与统计。
5. 验证数据可信度的 3 个硬核技巧:坐标系反推、shp 转 txt、压缩包哈希校验
拿到外部数据集,最容易被坑的是「数据打开没问题,但结论一算就跑偏」。最后一章说 3 个我在项目里常用的验证技巧。
首先,坐标系反推。如果.prj文件缺失,你要用坐标值的量级来判断。杭州中心的经纬度大约在经度 120.1、纬度 30.3 附近。打开 shp 的属性表,看坐标值如果在这附近,大概率是 EPSG:4326(经纬度);如果看到的是 2100000 附近的大数值(如 X: 21300000,Y: 3300000),那是 CGCS2000 3 度带投影坐标。偶尔还会遇到坐标值在 500000 附近,这是 UTM 的横坐标东移后的结果。用坐标值的直觉判断往往比盲目试坐标系更快,也更接近问题本质。
其次,shp 转 txt 导出检查。这里说的 txt 是通用的制表符分隔或逗号分隔文本,目的是用文本编辑器直接审查数据内容,绕开 GIS 软件的图形界面。用geopandas导出时注意编码:
import geopandas as gpd poi = gpd.read_file("data/hangzhou_poi.shp", encoding="utf-8") # 导出为 CSV 文件,注意编码用 utf-8-sig 方便 Windows Excel 打开 poi_text = poi.drop(columns=["geometry"]) # 去掉几何列,纯文本内容 poi_text.to_csv("poi_check.csv", index=False, encoding="utf-8-sig")导出后用 Excel 或wc -l检查行数和文本内容。这一步能发现 GIS 工具里看不见的问题,比如字段值里夹杂了不可见字符、制表符导致列错位、简介字段里带换行符等。这些在属性表里显示正常,但导出到外部系统时会导致解析失败。
最后,压缩包哈希校验。数据源提供方有时会在下载页同时给出 SHA-256 或 MD5 校验值。7z 文件在传输中可能损坏,损坏后的表现通常是解压到一半报「数据错误」,但也有个别情况是解压成功但内部某个 shp 文件已损坏。稳妥的做法是下载后先做哈希比对:
# Linux 下计算 sha256 sha256sum "杭州市2020年POI数据集.7z" # Windows PowerShell 下计算哈希 Get-FileHash -Algorithm SHA256 "杭州市2020年POI数据集.7z"两条命令都会输出一个 64 位的十六进制字符串,与下载页标注的哈希值逐字符比对。注意 7z 文件在压缩时如果用到了分卷(文件名如.7z.001),哈希校验要基于分卷合并后的完整文件,单独校验每个分卷没有意义。如果数据源没有提供哈希值,至少解压后对内部文件跑一次ogrinfo验证:
ogrinfo -so data/hangzhou_poi.shp hangzhou_poi输出中的Feature Count如果是个合理的整数,并且能正常列出字段列表,说明这个 shp 没有结构性损坏。把这一系列校验脚本固化成一个小工具集,以后再拿到类似的 POI 数据集或 DEM 数据,先跑一遍再谈分析,能省下大量排错时间。
本文还有配套的精品资源,点击获取