
简介南京市居住小区点数据Shapefile压缩包基于WGS84全球通用地理坐标系面向GIS初学者、城市规划、房地产与社会统计研究人员。数据以点要素表示各居住小区的中心位置或代表点方便与路网、行政区划等图层叠加可用于人口分布、社区配套、交通需求及宜居性评价等空间分析课题。压缩包共7个文件涵盖shp几何数据、dbf属性表、prj坐标定义以及sbn/sbx/shx空间索引等完整Shapefile组件整体仅643KB轻量易用兼容ArcGIS、QGIS等主流GIS平台。目前已有811人学习下载。借助这份数据读者可快速制作南京市居住小区分布专题图开展空间统计与密度分析并结合属性信息筛选、分级与专题研究为论文写作、市场研判或政务决策提供可靠的数据底图。1. 点数据与 WGS84 坐标系拆开南京市居住小区shp之前要想清楚的事拿到这份南京市居住小区shp数据包解压以后看到的不只有data.shp还有data.dbf、data.shx、data.prj、data.sbn、data.sbx和data.shp.xml。按描述这是WGS84坐标下的点数据也就是说南京市范围内的居住小区是以一个点来定位的而不是带完整边界的面要素。第一反应可能是点数据不如面数据直观但做统计分析时点的优势很明显小区质心之间的距离、小区与地铁站和学校的空间连接、按行政区划聚合小区数量几乎都是现成可算的。所以这份数据适合城市规划、房地产研究、社会统计这类偏向量化分析的场景也适合拿来作为学习Shapefile格式的完整案例。按这套流程走你能把zip包内每个文件的作用认全掌握用Python读取属性与坐标、做坐标系统一和空间叠加、批量转KML以及排查常见数据坑的方法。2. Shapefile 后缀文件的职责边界data.shp 到 data.sbx 怎么配合2.1 先认清这套文件是“一组数据”不是“一个文件”很多人在传输shp的时候习惯只发data.shp对方一打开就报错“无法打开要素类”。原因在于Shapefile是一个文件组几何信息在data.shp里属性在data.dbf里data.shx负责几何记录和属性记录之间的对应关系。如果把data.shx删了部分软件仍能读但每次按编号取要素时会明显变慢如果data.dbf丢了属性信息会全部消失如果data.prj丢了在叠加其他图层时会出现两个图层无法对齐的诡异局面。如果解压后内层还套了一个data.zip说明这份数据被二次打包过把内层也解出来按同一主文件名检查配套文件是否完整。文件存的内容缺失时的影响data.shp要素几何坐标无法打开data.shx几何与属性记录的索引读取变慢个别软件直接拒绝data.dbf属性字段与记录属性表丢失data.prj坐标系定义的WKT文本坐标系信息丢失data.sbn / data.sbx空间索引ArcGIS会提示重建索引data.shp.xml元数据仅影响元信息查看这套文件列表里的data.shp.xml是XML元数据文件一般由ArcGIS或第三方工具生成里面记录了制图日期、字段说明等补充信息对数据读取本身不构成影响。需要注意的是主文件名一旦改动所有配套文件必须同步改。例如把data.shp改成“居民小区.shp”data.dbf也必须改成“居民小区.dbf”否则索引文件找不到对应的几何记录。我见过不少人只改了其中一个文件名结果整个数据打不开最后只能重新解压zip包再处理。2.2 用 data.prj 验证坐标系是否真的为 WGS84拿到数据的第一件事是查看data.prj而不是急着打开地图预览。在命令行里直接查看文件内容cat data.prj如果看到类似下面的WKT文本说明坐标系确实是WGS84经纬度坐标GEOGCS[GCS_WGS_1984,DATUM[D_WGS_1984,SPHEROID[WGS_1984,6378137.0,298.257223563]],PRIMEM[Greenwich,0.0],UNIT[Degree,0.0174532925199433]]解释一下关键参数GEOGCS表示这是一个地理坐标系单位是度SPHEROID里的6378137.0是WGS84椭球的长半轴298.257223563是扁率分母PRIMEM是零经线默认是格林尼治。如果看到的是PROJCS而不是GEOGCS就说明数据是投影坐标系坐标单位不再是度而是米这种情况下和GPS设备采集的经纬度坐标叠加必须先做投影转换。所以data.prj不是可有可无的文件它决定了后续所有空间操作的基准。2.3 文件级排错先确认zip完整再怀疑数据本身如果解压失败常见原因不是“需要zip密码”而是zip包在下载或传输过程中不完整。如果下载源要求输密码先回到下载页核对来源不要在一个加密包上浪费时间。先用解压软件自带的“测试压缩包”功能验证完整性或者重新下载。解压完成后如果某个文件在解压时报CRC错误果断换源因为损坏的data.shp即使能解压出来后续读取也会产生奇怪的几何错误。为了快速判断一个shp是否可读我一般先用GDAL自带的ogrinfo查看概要ogrinfo -so data.shp data-so参数只输出概要信息包括要素数量、图层几何类型、字段列表不加载全部几何。如果这一步能正常返回基本可以排除文件头损坏的可能。乱码是另一个高频问题后面第5章会专门讲。这里先记住一个原则报错时先看文件是否齐全再看坐标系和编码最后才考虑重新下载按这个顺序排查效率最高。3. 用 Python 拆读居住小区数据geopandas 读取、属性过滤与 shp 转 txt3.1 环境准备装好 geopandas 和 pyshp读取shp最省事的方式是geopandas它的read_file方法内部封装了GDAL/OGR能直接读取几何和属性。建议同时装上pyshp用来做轻量级的字段读取和格式转换两者互补。pip install geopandas pyshp如果是在Windows环境下安装geopandas依赖的fiona或pyogrio经常出现版本不兼容的问题报错一般是DLL加载失败。这种情况更推荐用conda安装conda会统一处理GDAL的二进制依赖。装好以后先用一个最简单的脚本验证环境可用不要一次写太长。3.2 读取 data.shp 并确认字段与几何类型import geopandas as gpd gdf_utf8 gpd.read_file(data.shp, encodingutf-8) print(字段列表:, gdf_utf8.columns.tolist()) print(记录数:, len(gdf_utf8)) print(坐标系:, gdf_utf8.crs) print(gdf_utf8.head())这段代码读取data.shp后输出了字段名、记录数量和坐标系信息。字段列表里一般会包含小区名称、行政区代码之类的属性列具体有哪些字段以这份数据实际打印结果为准。如果打印出来的中文是乱码说明dbf里的编码不是utf-8改用GBK重新读取gdf_gbk gpd.read_file(data.shp, encodinggbk)注意这里说的是dbf字段编码的切换和shp几何文件没有关系坐标不会被编码影响。编码选择的标准很简单打印head后看中文是否正常。国内工具生成的shp常用GBKArcGIS默认读取也能识别但用Python时geopandas默认不走系统编码所以要显式指定。3.3 按属性过滤从散点中筛选特定小区拿到属性表以后最常见的需求是按条件筛选小区。比如根据名称包含的关键字提取目标范围或者结合年份字段筛出某时间段内的信息。下面给出一个通用写法# “小区名称”和“年份”请以自己打印出的字段名替换 if 小区名称 in gdf_gbk.columns: target gdf_gbk[gdf_gbk[小区名称].str.contains(新城)] print(名称中包含“新城”的小区数量:, len(target)) if 年份 in gdf_gbk.columns: recent gdf_gbk[(gdf_gbk[年份] 2015) (gdf_gbk[年份] 2024)] print(2015年之后的小区数量:, len(recent))这组筛选逻辑用str.contains做模糊匹配适合按名称关键字检索年份筛选用布尔条件组合返回的是满足全部条件的子集。需要提醒的是dbf字段名一般不超过10个字符年代久远的工具生成的数据字段名可能被截断打印字段列表以后先对照避免直接拿完整中文列名去过滤。3.4 导出 txt / csv 作为中间分析文件shp转txt是很多人在拿到数据后的第一步文本格式方便在Excel里查看也方便写脚本清洗。从geopandas导出坐标文本的做法是先取出经纬度列再写出CSVgdf_gbk[lon] gdf_gbk.geometry.x gdf_gbk[lat] gdf_gbk.geometry.y gdf_gbk.to_csv(南京居住小区.txt, sep|, indexFalse, encodingutf-8-sig)这里geometry.x和geometry.y分别取的是每个点要素的经度和纬度前提是数据坐标系为WGS84。如果坐标系不是经纬度导出之前要先to_crs(4326)否则得到的x、y是投影平面坐标。sep|可以避免字段值里出现逗号时把列拆错encodingutf-8-sig会在文件头写入BOM这样用Excel打开时中文不容易乱码。如果不想写Python用GDAL的命令行工具也能一步完成ogr2ogr -f CSV 南京居住小区.csv data.shp -lco GEOMETRYAS_XY-lco GEOMETRYAS_XY表示把每个点的坐标输出成两列隐含的前提是数据坐标系必须是经纬度。这个方式适合批量处理把data.shp换成其他文件名循环执行即可。4. 坐标统一与空间叠加把 WGS84 点数据落进南京区划底图4.1 叠加前先确认底图的坐标系别被 GCJ-02 带偏移把点数据叠加到区划底图上最常踩的坑是坐标系不一致。很多在线地图服务输出的坐标是GCJ-02这套坐标在WGS84的基础上做了偏移直接把WGS84的点叠上去整体会偏移几十到几百米视觉上明显不在道路或建筑范围内。判断方法是用pyproj检查当前坐标系的定义from pyproj import CRS crs CRS.from_user_input(gdf_gbk.crs) print(crs.name) # 输出坐标系名称 print(crs.axis_info) # 输出坐标轴、单位信息如果axis_info显示单位是degree说明当前是经纬度坐标可以直接与WGS84底图叠加。如果底图来自在线地图且明显和点位置对不上优先怀疑底图是GCJ-02而不是数据本身有问题。还有一种情况是底图是投影坐标单位是米与经纬度数据直接叠加会完全错乱这种情况需要把点数据转换到投影坐标系gdf_utm gdf_gbk.to_crs(epsg32650)EPSG:32650对应WGS84 / UTM zone 50N坐标单位是米南京大部分区域落在50N范围内。转换后计算距离、做缓冲区都以米为单位逻辑上更直观。这个投影适合做中尺度的面积和距离计算但做跨大区域的统计时不适用。4.2 空间连接统计每个行政区内的小区数量拿到南京市区划面数据后用空间连接可以快速统计每个区的小区数量。这里假设你已经有一份南京市或各区的面状shp路径换成自己本地实际路径district gpd.read_file(nanjing_district.shp, encodingutf-8) district district.to_crs(gdf_gbk.crs) joined gpd.sjoin(gdf_gbk, district, howleft, opwithin) stats joined.groupby(区名).size().reset_index(name小区数量) print(stats)代码先读取区划面数据再用to_crs把面数据统一到点数据的坐标系下确保两个图层在同一个空间基准里。sjoin的opwithin表示只保留落在面内部的点区划边界上的点有时会被舍弃如果某个点的“区名”字段为空可以先给点做一个极小的缓冲区再连接或者检查底图边界是否封闭。提示如果“区名”字段有空值优先检查点是否落在区划边界上再检查底图坐标系是否和点数据一致。stats输出的是每个区的记录数也就是该区内居住小区点的数量这个结果可以直接用来做柱状图或专题图。4.3 用缓冲区把点数据还原成可分析的面点数据的局限是看不到小区边界做覆盖范围分析时可以给每个点生成缓冲区用缓冲区近似表示小区影响范围。buf_300m gdf_gbk.copy() buf_300m.geometry gdf_gbk.geometry.buffer(0.0027)在WGS84经纬度坐标系下buffer的单位是度0.0027度大约对应300米。下面是常用半径的换算参考按1度约111公里估算目标半径经纬度增量近似距离100米0.0009约100米300米0.0027约300米500米0.0045约500米1公里0.009约1公里在南京纬度32度附近经度方向的1度实际距离约为111公里乘以cos(32度)约94公里所以上表在经度方向会偏差8%左右。如果需要比较精确的半径先投影到EPSG:32650再按米设置buffer例如buffer(300)代表300米。生成缓冲区之后可以继续做面积计算、覆盖范围统计或与公共服务设施点做叠置分析。4.4 和其他公开shp数据联合使用的思路如果你手里还有道路数据比如公开下载的2020年全国道路交通shp想分析小区到主干道的距离处理思路是一样的先统一CRS再做距离计算或空间连接。道路数据往往是大范围图层数据量大会拖慢计算建议先按行政边界裁剪再做距离分析。裁剪可以用clip操作clipped_road gpd.clip(road_gdf, district_boundary)裁剪后再做road_gdf.distance(buf_300m.geometry)得到的就是每个缓冲区到最近道路的距离单位取决于当前坐标系。这里强调一点任何两个shp图层叠加前都要先把crs打印出来对比统一坐标系以后再执行操作这一条能避免大部分“叠加不上”的问题。5. SHP 数据的日常坑批量压缩、无效几何与乱码排查5.1 批量压缩单个shp的正确姿势当手头有多个shp文件需要分发时不能只把data.shp拖进zip包必须把配套文件一起打包否则对方打开只有一个空壳。用Python可以按主文件名批量打包避免漏掉dbf和prjimport zipfile from pathlib import Path for shp_path in Path(.).glob(*.shp): stem shp_path.stem with zipfile.ZipFile(f{stem}.zip, w, zipfile.ZIP_DEFLATED) as zf: for suffix in [.shp, .shx, .dbf, .prj, .sbn, .sbx, .shp.xml]: f shp_path.with_suffix(suffix) if f.exists(): zf.write(f, arcnamef.name)这段脚本遍历当前目录下所有shp文件每个shp生成一个同名zip包把该shp对应的全套配套文件都写进压缩包。arcnamef.name表示压缩包内只保留文件名不保留目录层级解压后所有文件在同一目录方便直接使用。ZIP_DEFLATED是常规的deflate压缩算法shp是二进制几何数据压缩率一般不高但能避免传输过程中文件被误删或漏发。sbn和sbx不是所有数据都有用exists判断可以跳过缺失项。5.2 无效几何修复buffer(0)在面数据上的用途做面数据叠加时偶尔会遇到sjoin报错提示“geos_prepared_error”或类似信息多半是几何存在自相交问题。一个通用的修复方法是给几何做零距离缓冲gdf[geometry] gdf.geometry.buffer(0)buffer(0)不会改变几何形状但会重建多边形边界把自相交多边形转化为有效几何从而让空间关系运算正常执行。这份南京居住小区数据本身是点数据点几何不存在自相交问题这一步主要是在把点缓冲区转成面或合并其他面要素时使用。需要说明的是它解决的是拓扑有效性问题解决不了坐标系错乱和属性乱码。5.3 乱码排查utf-8、gbk和dbfread三层方案中文属性乱码在shp里非常常见原因在于dbf文件本身没有强制编码声明读取工具只能靠猜。排查我按三个层级处理先用utf-8读看字段名和值是否正常不正常再用gbk读如果两个都异常用dbfread按原始字节读取看底层数据到底是什么内容pip install dbfreadfrom dbfread import DBF table DBF(data.dbf, encodinggbk) for record in table[:5]: print(record)dbfread会把每条记录以字典形式输出key是字段名value是解码后的值。对比不同编码的输出结果很快能判断dbf实际用的编码。注意有些数据在生成时字段编码是gb18030这是gbk的超集遇到生僻字乱码时可以用encodinggb18030尝试。5.4 格式转换里的几个误区从dwg转换shp常见错误是直接把扩展名改成shp这不会改变文件内部结构打开仍然会失败。常见做法是先在CAD里另存为dxf再由GIS软件的导入CAD功能完成转换中间尽量不要跳过DXF这一层。栅格数据也不适合硬转shp像地下水位这类栅格数据用GeoTIFF等栅格格式保存才能保留像元值硬转成shp只会得到一堆点和孤立的几何丢失完整的空间连续信息。道路shp、遥感解译矢量这类数据和本套居住小区数据本质上是同一套文件规范排错思路完全通用先查齐文件再看坐标系最后处理编码问题。6. shp 转 KML 与核密度估算给居住小区点数据加一层分析价值6.1 用脚本批量转 KML 供现场核对外业核查时KML可以直接被奥维地图、Google Earth、手机地图App打开是携带shp的轻量替代方式。用simplekml写一个转换脚本pip install simplekmlimport simplekml kml simplekml.Kml() for idx, row in gdf_gbk.iterrows(): name str(row.get(小区名称, idx)) p kml.newpoint(namename, coords[(row.geometry.x, row.geometry.y)]) p.style.iconstyle.color simplekml.Color.red kml.save(南京居住小区.kml)这里newpoint的coords参数接收的是[(经度, 纬度)]列表几何类型是点所以直接取x、y。row.get如果字段里没有“小区名称”就用行号兜底避免脚本中断。KML文件内部使用UTF-8编码中文名称基本不会再出现乱码。批量转换多个shp时在外层再包一个循环对每个shp调用同样的逻辑即可这也对应shp批量转kml的常见诉求。6.2 用核密度估算小区聚集特征居住小区点的空间分布能反映城市建成区的重心。用高斯核密度做一个快速估计可以看到各板块的居住密度差异from scipy.stats import gaussian_kde import numpy as np coords np.vstack([gdf_gbk.geometry.x, gdf_gbk.geometry.y]) kde gaussian_kde(coords, bw_method0.02) grid_x np.linspace(coords[0].min(), coords[0].max(), 200) grid_y np.linspace(coords[1].min(), coords[1].max(), 200) X, Y np.meshgrid(grid_x, grid_y) Z kde(np.vstack([X.ravel(), Y.ravel()])).reshape(X.shape)bw_method是带宽系数值越小密度面越尖锐0.02在经纬度尺度下能看到局部的小尺度聚集值改到0.1则整体平滑更适合看城市级格局。把Z保存成GeoTIFF后可以和道路shp做叠加分析居住密度与路网等级的关系。这个方法本质上和渔网分割做网格统计互补渔网是固定网格聚合数量核密度是连续密度曲面两者输出形式不同结论互通。6.3 导出前的坐标验证习惯转KML或栅格之前最后再验证一次坐标基准。执行下面的命令检查data.prj中的坐标系定义是否还是WGS84head -c 300 data.prj看到GEOGCS且单位是Degree就可以放心导出看到PROJCS先执行gdf_gbk.to_crs(epsg4326)再导出否则KML里点的位置会偏出几公里。如果data.prj里的Datum字段不是WGS_1984同样先执行to_crs(epsg4326)再导出KML否则点位的经纬度会整体偏移数百米。本文还有配套的精品资源点击获取