简介:这份上海市2020年POI矢量数据集,整合全市兴趣点、30米分辨率DEM与行政区划边界,面向GIS分析师、城市规划师及地图开发者,可用于地形模拟、商业选址和公共服务评估。数据同时提供Shapefile和Excel两种格式:前者含shp/dbf/prj等组件,适合ArcGIS、QGIS空间分析与编辑;后者为xlsx表格,便于业务人员直接做统计筛选。压缩包共126个文件,除上述格式外还包括tif高程栅格及配套文件,整体约80.65MB。目前已有815人学习下载。借助区县边界、POI分类与DEM,可快速开展人口分布对照、设施可达性、坡度日照等城市空间量化分析,双格式设计兼顾了专业制图与日常办公需求,是理解2020年上海城市格局的实用数据基础。
1. 拿到这个7z包先别急着解压:上海2020年POI数据集里到底有什么
做GIS和空间数据分析的人,一定经历过为凑一套完整基础数据在各论坛和网盘之间来回折腾的日子。这个标题里出现的上海市2020年POI矢量数据集,不只是一堆点,它是把三样东西打包在了一起:带空间位置的shp格式POI、方便按属性筛选和二次编辑的excel格式POI,外加一张30米分辨率的DEM高程栅格和行政区划边界。换句话说,拿到这个包,你既能画设施分布,也能算地形起伏,还能按行政区做裁剪统计。它适合规划院做现状调研、商业选址看周边配套、高校做课程设计,也适合刚转行做空间数据分析的人拿来做入门练手。在解压之前,先把包里可能是什么、怎么拆、拆完先检查什么这两件事弄清楚,能帮你省下后面一大半的返工时间。
2. 拆包与盘货:7z解压、目录结构与字段的第一次摸底
2.1 为什么是7z而不是zip:高压缩率与解压口令
全量POI动辄几十万条记录,属性表里还有类别、名称、地址、电话等长文本字段,原始shp和excel合起来很容易超过1GB。用7z压主要看中两点:一是压缩率高,同一份POI数据用7z压完通常比zip再小20%到30%,适合网盘传输;二是它支持分卷和加密,如果发布者做了分卷,你得把所有分卷放在同一目录下再解压。
在Linux服务器上解压时,我一般用p7zip提供的7z命令。核心命令是:
7z x 上海市2020年POI矢量数据集.7z -o./shanghai_poi_2020 -yx表示解压并保留目录结构,-o指定输出目录,注意-o后面不要加空格,这是7z一贯容易踩的坑。-y是在遇到覆盖询问时自动同意,批量解压省得盯着终端。如果压缩包设置了密码,追加-p密码;不想让密码出现在终端历史里,就只写-p,回车后交互输入。
解压完成后,用7z l查看压缩包内的文件清单,再对一下解压出来的文件数是否一致,这是验证压缩包是否完整的最快办法。Windows下不需要记命令,装个7-Zip或者Bandizip,右键解压到指定文件夹就行。但有一个规则两边通用:先解压到纯英文路径,比如D:\data\shanghai_poi_2020,不要直接解压到桌面或带中文的深目录,后面用ArcGIS或QGIS打开时,中文路径对底层库的支持时好时坏,容易出莫名其妙的打不开。
2.2 解压后先盘货:我给数据集建的索引表
解压完不要急着双击shp,先打开文件夹,把里面的文件按类型列成一张索引表。这类数据包通常包含四类内容,我用一张表概括:
| 文件类型 | 扩展名 | 作用 | 打开方式 |
|---|---|---|---|
| POI矢量 | .shp/.dbf/.shx | 空间点位,可叠加分析 | ArcGIS / QGIS |
| POI表格 | .xlsx/.xls | 按字段筛选、透视统计 | Excel / WPS |
| DEM栅格 | .tif/.img | 高程、坡度、坡向分析 | ArcGIS / QGIS |
| 行政区划 | .shp/.dbf | 裁剪、分区统计边界 | ArcGIS / QGIS |
shp有个容易被新手忽略的规则:它不是一个单独的文件,而是由.shp、.dbf、.shx三个基础文件组成的文件组,.shp存几何、.dbf存属性、.shx存索引。如果是带投影的文件,还会多一个.prj。所以盘货时要看它是不是一个完整文件组,只拷走一个.shp在别人机器上照样打不开。
盘完文件类型,接着要建字段字典。双击打开excel版POI,把字段名列出来:比如名称、地址、类别、经度、纬度、区县等。这一步看起来原始,但它决定了后面你做空间连接时用哪个字段挂接。同时打开dbf属性表,对比两边字段名和字段顺序是否一致。字段类型也要看一眼,重点确认经纬度是数值型还是文本型,是十进制度还是度分秒格式。这两个问题不查清楚,第4章的导入转shp环节一定会翻车。
2.3 字段摸底:excel和shp里的POI字段要分开看
拿到excel格式POI后,先看工作表底部有几个sheet,常见的布局是第一个sheet放字段说明,第二个sheet放全量数据,也可能按类别拆成多个sheet。我一般先用Python做一次快速扫描,把列名、行数、每列的非空值数量一次打出来:
import pandas as pd df = pd.read_excel("上海_poi_2020.xlsx", sheet_name=0, nrows=5) print(df.dtypes) df_full = pd.read_excel("上海_poi_2020.xlsx", sheet_name=0) print("总记录数:", len(df_full)) print("每列非空数:\n", df_full.count())nrows=5只读前5行,用于快速确认字段名和列顺序;第二次全量读入后,用count()检查每一列有没有大面积的缺失值。如果经纬度列存在缺值或空值,转shp时那些点会被几何引擎直接丢弃,但你是看不到被丢弃了多少的——所以最好在导入前就把缺值比例搞清楚。如果发现坐标字段是文本格式,比如"121.4737"前后带着不可见空格,要先清理:
df["lon"] = df["经度"].astype(str).str.strip().astype(float) df["lat"] = df["纬度"].astype(str).str.strip().astype(float)astype(str)先把所有值统一转成字符串,strip()去掉首尾空格,再转浮点。这步叫“清洗坐标列”,等真正导图的时候如果点位飞到非洲或显示不出来,十有八九就是这一步没做干净。
3. 坐标系与投影对齐:把三份数据放到同一张地图上
3.1 先判断坐标系再动手:WGS84、GCJ02还是CGCS2000
这个数据包里POI、DEM、行政区划大概率不是同一套坐标系,这是空间分析里最容易出问题却最不容易被发现的坑。坐标系的差异不像字段缺失那样会在日志里报错,它的典型表现是“数据能打开,但叠加后点位整体偏移几百米到几公里”,而新手最容易忽略它,直接认为是数据下载错了。
拿到数据的第一个动作,是先看.prj文件里的投影信息。用文本编辑器打开.prj文件,里面是WKT格式的描述。常见的三种可能我列成一张表:
| 坐标系 | 典型标识 | 坐标量级 | 常见来源 |
|---|---|---|---|
| WGS84 | GEOGCS["GCS_WGS_1984"] | 经度120~122,纬度30~32 | GPS采集、国际开放数据 |
| GCJ02 | 无标准prj,常被标成WGS84 | 经度121.4附近,偏移约200~500米 | 国内地图平台导出 |
| CGCS2000 | GEOGCS["China_2000"] | 经度120~122,与WGS84差异约几十厘米 | 国土、规划系统成果 |
如果.prj文件缺失,还有一个土办法判断:把POI和行政区划同时加载进ArcMap,看看点位和区县边界是否贴合。如果点在行政区范围外几百米,大概率是GCJ02加密坐标被错标成了WGS84;如果POI点位整体落在街区内部、和路网关系正常,基本可以判断是WGS84或CGCS2000。用上海人民广场的坐标做参照也行——WGS84下大约是东经121.47、北纬31.23,如果你的POI数据围绕这个值浮动不超过0.01度,说明没加密;如果统一偏移了0.005度以上,就要警惕GCJ02。DEM栅格一般不再加密,大多是CGCS2000或WGS84,可直接从.tif的元信息里读。
3.2 坐标对齐实操:ArcGIS的Project和QGIS的warp
先统一逻辑:把矢量数据和DEM全部转到一个目标坐标系,不要问哪个坐标系“最好”,要看你的用途。如果做的是城市尺度叠加分析、出图比例尺在1:1万到1:10万之间,用CGCS2000的投影坐标系最稳妥;如果只是把POI点落在WGS84底图上做初步可视化,全部转WGS84地理坐标系就够了,不上投影还省事。
ArcGIS下用ArcToolbox里的Data Management Tools -> Projections and Transformations -> Feature -> Project,输入POI图层,在Output Coordinate System里选择目标坐标系,比如CGCS2000 / 3-degree Gauss-Kruger zone 38——上海经度在121度附近,3度带的中央经线对应120度或123度,带号要选对,选错了整个图会横向错位。这里的坑是很多人不清楚EPSG代码,推荐查询目标坐标系的EPSG编号再填入,少走弯路。
QGIS里的对应操作是Vector -> Data Management Tools -> Reproject Layer,输出图层会自带新的坐标系。栅格DEM则用Raster -> Projections -> Warp (Reproject)。warp参数里要注意“Resampling method”选Bilinear(双线性插值),因为DEM是连续地形表面,用最近邻(Nearest Neighbour)会出现明显的阶梯状断层。
对齐完成后做一次验证:用ArcGIS的Identify工具随机点选10个POI点位,对照底图上的建筑或道路交叉口,确认点位没有出现可见偏移。如果是从GCJ02转WGS84,转换并不是简单的减偏移量,而是需要先做坐标系变换,很多公开的地图SDK里才有对应函数。我见过有人直接用固定常数把坐标平移,结果郊区点位仍然偏移大几十米——数学上完全不对,别省这一步。
4. 从Excel POI到可分析shp:导入、投影与空间连接
4.1 用Python把Excel POI转成shp(pandas + geopandas)
标题里同时给了shp和excel两种格式的POI,为什么要两版?shp适合在GIS里做空间查询、裁剪和叠加,excel适合用数据透视表做类别统计、物业类型占比、连锁品牌覆盖等常规业务分析。但很多实际工作是先从excel里筛完一批目标POI,再把这批目标转成shp去做空间分析,所以Excel到shp这条路是绕不开的。我一般用geopandas来做,步骤简洁且可复现:
import pandas as pd import geopandas as gpd from shapely.geometry import Point df = pd.read_excel("上海_poi_2020.xlsx") df = df.dropna(subset=["经度", "纬度"]) geometry = [Point(lon, lat) for lon, lat in zip(df["经度"], df["纬度"])] gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") gdf = gdf.to_crs("EPSG:3857") # 如需投影坐标系再执行 gdf.to_file("上海_poi_2020_筛选.shp", encoding="utf-8", driver="ESRI Shapefile") print(f"生成{len(gdf)}个点,已导出shp")这段代码的核心就四步:dropna剔除经纬度缺失的行;Point构造空间几何;GeoDataFrame把普通表升级为带几何的矢量表;to_file写出shp。crs="EPSG:4326"在创建时就要指定,否则后面任何空间操作都会报缺少坐标系的错误。最后写文件时给encoding="utf-8",是为了避免属性表中文乱码,这个参数只影响dbf内部编码,不影响坐标数据本身。
如果你手里只有ArcGIS没有Python环境,也有等价操作:右键Excel文件 ->Export Features或使用Excel To Table工具,把它转成dBASE或地理数据库表,再用Display XY Data选经纬度字段生成事件层,最后Export Data导出成shp。步骤多几步,但逻辑相同,关键还是创建事件图层时别选错坐标字段和坐标系。
4.2 空间连接与行政区裁剪DEM:一张可自由出图的分析底图
shp版POI拿到后,最常见的操作是把POI关联到行政区划,统计每个区的POI数量或类别构成。这里用不上“点对区”的拓扑计算,直接做空间连接即可。ArcGIS里工具是Spatial Join,目标图层选POI,连接图层选行政区划,匹配选项选Intersect,输出结果中每条POI会带上区县代码和区县名称字段。QGIS里对应的是Join attributes by location,结果同理。
空间连接做完后,建议把结果导出成新文件再继续分析,因为空间连接生成的临时图层一旦关闭,关联字段就丢失了。这一步常被忽略,我自己的习惯是任何分析结果的第一时间就落盘,别让临时图层在工程里裸奔。
接下来是DEM的处理。30米分辨率的DEM适合做整个上海市域的缓坡分析和宏观地形起伏,但如果要针对某个区分析,直接用原文件做切片会非常慢,正确做法是先用行政区面图层裁剪DEM。ArcGIS里用Extract by Mask,输入栅格选DEM,输入掩膜选行政区划面,输出就是被裁剪到该区范围的高程栅格。注意这里区分两个工具:Clip是按矩形范围裁剪,Extract by Mask是按面形状裁剪,前者的输出是外接矩形,后者才是真正的行政区边界形状。很多人在这个问题上分不清,导出来的DEM范围比行政区大一圈,后续做填挖方分析时结果全错。
QGIS里对应Raster -> Extraction -> Clip Raster by Mask Layer,同样指定裁剪图层。裁剪后检查一下输出栅格的NoData值设置,凡是落在行政区边界外的地方,栅格值应为NoData而不是0,否则坡度和填挖方计算会把NoData当成平地处理。
4.3 清洗后的POI分类字段写回excel备份
做完空间连接和裁剪,POI属性表里多了行政区字段,这时顺手把结果导出回excel一份,作为后续做报表的底表,比直接在GIS里做数据透视表顺手得多。geopandas写法:
result = gpd.read_file("上海_poi_2020_筛选.shp") result = result.drop(columns=["geometry"]) # 去掉几何列,存excel result.to_excel("上海_poi_2020_分析底表.xlsx", index=False)完整空间分析工作流是用shp完成,最终汇报交接用excel,两种格式各有分工,别试图用一种格式包打天下。这里还有个小技巧,导出前先用result["区县"].value_counts()看下各区的POI数量分布是否合理,如果某个区的数量占比高得离谱,回头查一下数据是否在空间连接时出了问题,比如某个面要素存在重复边界导致多点落入。
5. 避坑:这个数据集的六宗最常见翻车现场
5.1 解压报错,分卷缺失或CRC校验失败
现象:解压到一半弹出“缺少分卷”或“CRC错误”,压缩包被中断,已解压出来的文件不完整。原因:下载时传输中断,或者网盘客户端没有把分卷文件全部同步到本地。解决:先对比本地分卷文件大小和网盘页面展示的是否一致,补下缺失卷;用7z t 文件名.7z测试压缩包完整性,输出Everything is Ok再用,不要赌运气硬解压。
5.2 shp打不开,提示shapes与table records数量不匹配
现象:打开shp时提示“Number of shapes does not match number of table records”,地图上只显示了一部分点。原因:shp文件组被单独拷贝过,.dbf和.shp不同步,或者解压不完整。解决:回到原始7z包重新完整解压;如果文件名是中文,改成英文文件名再打开;还打不开就用ogrinfo检查文件结构,确认是哪个文件损坏,重新下载对应分卷。这类问题别自己手工去编辑dbf补记录,越补越乱。
5.3 excel里的经纬度是文本,shp转出来点位全在非洲
现象:Excel中经纬度列显示为“121.4737E”,带字母或单位,用pandas读取后直接astype(float)报错。原因:POI数据源在写入时把坐标拼成了带后缀的文本,或者小数点是全角字符。解决:先统一文本格式,提取数字部分再转换。处理全角字符可以做一个整列清洗:
df["经度"] = df["经度"].astype(str).str.replace(".", ".").str.extract(r"([\d.]+)").astype(float)str.extract配合正则只保留下数字和小数点,前后缀全部丢弃。转换完后核对取值范围,经度应在120~122之间、纬度在30~32之间,超出这个区间直接打印告警,别让脏坐标混进后续分析。
5.4 POI点整体偏移,落不到行政区范围里
现象:POI和行政区划叠加后,点位全部向东南或西北偏移几百米。原因:前面第3章说的坐标系不一致——POI是GCJ02,行政区划是CGCS2000,或者POI是WGS84,被投影工具强行按GCJ02读取。解决:先各自用Identify查看坐标值,确认两个图层的坐标系定义,再做统一转换。记住一个关键点:GCJ02是社会公开的电子地图坐标系,坐标系变换不能只做平移,需要先按照公共转换参数迭代计算,至少要把经纬度先转成平面坐标再变换。没有现成转换工具时,直接放弃让点位完美贴合道路,退一步做行政区级别的统计也够用。
5.5 dbf属性表中文乱码,全是问号
现象:ArcGIS里打开POI属性表,中文名称、地址字段显示为“???”,或者变成类似“?¨?”的乱码。原因:dbf文件默认编码与ArcGIS当前区域设置不一致,常见的是源数据用GBK编码写入,ArcGIS按UTF-8读取。解决:不要直接在ArcGIS里改配置。用Python读出来后重新以UTF-8编码写一份shp即可;如果手里只有ArcGIS,可以在ArcToolbox -> Data Management Tools -> Add Field里重建字段再从原表计算灌入,虽然繁琐但有效。习惯上我现在写任何shp都强制指定encoding="utf-8",从源头上杜绝乱码。
5.6 用面图层裁剪DEM,输出一片黑或整片无值
现象:Extract by Mask执行成功,但裁剪出的DEM在ArcMap里显示为一整块黑色或空白。原因:栅格范围只有极少数像元,或者输出栅格被限制在面要素外接矩形的瞬间时,裁剪范围没有与DEM像元对齐,产生大量NoData像元且显示比例被拉伸到异常值。解决:在Extract by Mask的环境设置中,把Snap Raster设为原始DEM,这样输出像元会严格对齐到输入DEM网格;如果仍然偏黑,检查属性表里最小最大值是否正常,必要时用Calculate Statistics重建栅格统计信息。黑屏多数不是数据坏了,而是符号化显示没刷新而已。
6. 进阶:用Python给数据集做一次强制体检,十分钟摸清底细
分析做完之前,先给整个数据集写一个体检脚本,把坐标系、字段、数量、经纬度范围、行政区和DEM边界一次查明,后续所有的分析工作都在这份体检报告之上做决定,避免中途再来回折腾。
import geopandas as gpd import pandas as pd import rasterio def check_vector(path, name): gdf = gpd.read_file(path) print(f"[{name}] 要素数: {len(gdf)}, 坐标系: {gdf.crs}") print(f"范围: {gdf.total_bounds}") return gdf def check_raster(path, name): with rasterio.open(path) as src: print(f"[{name}] 像素: {src.width}x{src.height}, 坐标系: {src.crs}") print(f"范围: {src.bounds}, NoData: {src.nodata}") gdf_poi = check_vector("上海_poi_2020.shp", "POI") gdf_district = check_vector("行政区划.shp", "行政区") check_raster("上海_dem_30m.tif", "DEM")脚本虽短,输出却足以支撑判断:如果total_bounds中南边界大于31.3度,说明坐标可能混入了GCJ02的非标准范围;如果DEM的宽度和高度比例明显不符合经纬度跨度比例,说明它可能已经做过投影或重采样。这些信息不需要打开GIS就能拿到,效率差得很明显。
我自己的习惯是每次拿到外部数据集,先跑一遍这种体检,把结果存成一个data_profile.txt放在数据目录下,接下来的所有分析步骤都对照它来定参数。比如第3章的转换目标、第4章的行政区分组,都要参考这份报告决定,而不是打开软件边看边试。数据集的坑基本都藏在边界值、坐标系和字段类型里,这三个点查明白,后面的分析就稳了。
坐标范围确认后,再用行政区边界对POI做一次空间过滤,剔除落到辖区外的孤立点,得到干净的分析底图。这套流程走下来,整个数据集的底细就完全掌握在手里,后续无论是转3dtiles做可视化,还是做渔网统计POI密度,都能直接拿现成的数据源继续。希望这套从解压到体检的路径能帮你少走弯路,把时间留给真正的分析工作。
本文还有配套的精品资源,点击获取