據(jù)包解析:QGIS加載、坐標(biāo)系修正與GeoJSON導(dǎo)出)
簡介甘肅省基礎(chǔ)地理信息矢量數(shù)據(jù)包以Shapefile格式組織面向GIS初學(xué)者、規(guī)劃人員及地理數(shù)據(jù)分析者可用于地圖制圖、空間查詢與基礎(chǔ)分析。包內(nèi)共225個(gè)文件含39組shp、shx、dbf、prj配套文件覆蓋相同數(shù)量的要素圖層另有sbn、sbx空間索引以及mxd工程文檔壓縮包大小17.3MB。數(shù)據(jù)涵蓋點(diǎn)、線、面多種幾何類型包括火車站、公路、國道、縣鄉(xiāng)道、單線水系、省縣界、加油站、旅游景點(diǎn)等可支撐交通網(wǎng)絡(luò)分析、行政區(qū)劃展示、資源分布評(píng)估等場景。已有3031人學(xué)習(xí)下載目錄按要素拆分、命名規(guī)范便于按需調(diào)用。通過ArcGIS或QGIS可直接加載并可進(jìn)行疊加分析、緩沖區(qū)分析等空間操作也可轉(zhuǎn)換為GeoJSON等格式用于Web可視化是理解甘肅省地理空間特征、開展區(qū)域研究與規(guī)劃工作的實(shí)用基礎(chǔ)數(shù)據(jù)包可直接用于項(xiàng)目底圖與分析。1. 甘肅基礎(chǔ)數(shù)據(jù)包先搞清楚這套shp能拆出什么拿到“甘肅基礎(chǔ)數(shù)據(jù)完整版矢量shp格式”這個(gè)壓縮包先說一句可能打消你疑慮的話里面不是一張柵格底圖也不是在線地圖的截圖而是標(biāo)準(zhǔn)的Shapefile矢量數(shù)據(jù)。做GIS的同事看到那一排.dbf后綴就知道——每一層的幾何圖形和屬性表分開存放加載后能篩選、裁剪、按字段查詢也能直接導(dǎo)出成GeoJSON或KML。我的建議是先別急著畫圖把壓縮包里的要素層逐個(gè)加載一遍確認(rèn)坐標(biāo)系和屬性表編碼再談出圖和分析。這篇文章同時(shí)覆蓋兩類人用QGIS做項(xiàng)目地圖的從業(yè)者以及正在做地理空間分析的學(xué)生無論哪一類都需要先想明白“這套數(shù)據(jù)能拆哪幾層、坐標(biāo)是什么、亂碼怎么修”。2. 壓縮包里裝了什么十類要素層與Shapefile三件套2.1 Shapefile不是單文件每個(gè)圖層至少要湊齊三件套很多第一次接觸GIS數(shù)據(jù)的人會(huì)問為什么解壓出來一堆同名文件到底該拖哪個(gè)進(jìn)軟件答案很直接拖.shp后綴的那個(gè)其他文件是它的“隨從”。一套完整的Shapefile由多個(gè)伴隨文件組成缺了任何一個(gè)都可能讓圖層打不開或者屬性表變空。擴(kuò)展名作用缺失后果.shp存儲(chǔ)幾何信息點(diǎn)、線、面的坐標(biāo)圖層完全無法加載.shx幾何索引按記錄號(hào)定位坐標(biāo)某些軟件提示“損壞”或無法顯示.dbf屬性表每一行的字段值都在這里圖層能看到形狀但屬性表空白.prj坐標(biāo)系統(tǒng)描述WKT格式文本軟件會(huì)“猜”坐標(biāo)系經(jīng)常猜錯(cuò).cpg屬性表編碼聲明常見GBK/UTF-8中文屬性亂碼這套壓縮包里的“地名.DBF”“公路.DBF”這些清單實(shí)際上對(duì)應(yīng)的是“地名.shp”這些圖層的屬性表文件。你在解壓時(shí)如果發(fā)現(xiàn)某個(gè)圖層只有.dbf而沒有.shp說明文件在傳輸時(shí)被拆分或者丟失這個(gè)圖層就只能當(dāng)表格用無法畫到地圖上。2.2 十類要素層的部門劃分點(diǎn)、線、面分別能干什么從壓縮包的文件清單來看這套數(shù)據(jù)至少包含十種要素層。把它們按幾何類型歸類使用思路就清晰了線要素公路、國道、省道、縣鄉(xiāng)道、單線水系。這些層表示道路和水系的中心線或邊線可以用來做路網(wǎng)連通性分析、路徑規(guī)劃底圖、水系分布研究。點(diǎn)要素地名、鄉(xiāng)鎮(zhèn)名、加油站、郊區(qū)旅游景點(diǎn)。這些是POI性質(zhì)的數(shù)據(jù)適合做設(shè)施分布密度、可達(dá)性分析或者直接作為地圖標(biāo)注點(diǎn)。面要素建筑物區(qū)域。閉合多邊形可以用于城市建成區(qū)提取、建筑面積統(tǒng)計(jì)、地塊邊界參考。這里有一個(gè)容易被忽略的細(xì)節(jié)壓縮包里沒有單獨(dú)的“省界”“縣界”圖層只有鄉(xiāng)鎮(zhèn)名和地名。如果你需要精確的行政邊界常見做法是拿鄉(xiāng)鎮(zhèn)名圖層做標(biāo)注基準(zhǔn)再補(bǔ)充一套行政界線shp或者從鄉(xiāng)鎮(zhèn)名坐標(biāo)點(diǎn)做泰森多邊形得到粗略勢力范圍。別指望一套基礎(chǔ)數(shù)據(jù)把行政區(qū)劃邊界也給你畫好。2.3 用腳本快速摸清每個(gè)圖層的字段結(jié)構(gòu)拿到壓縮包后我習(xí)慣先跑一段Python腳本把每個(gè)圖層的要素類型、記錄條數(shù)和字段名列出來。這樣能提前發(fā)現(xiàn)“這個(gè)圖層能不能做屬性篩選”“哪個(gè)字段適合做標(biāo)注”。用pyshp庫最輕量import os import shapefile data_dir 甘肅基礎(chǔ)數(shù)據(jù)/shp # 改成你解壓后的實(shí)際目錄 for fn in os.listdir(data_dir): if fn.endswith(.shp): base fn[:-4] try: sf shapefile.Reader(os.path.join(data_dir, fn)) print(f圖層: {base}) print(f 要素類型代碼: {sf.shapeType} (1點(diǎn), 3線, 5面)) print(f 記錄數(shù): {len(sf)}) field_names [f[0] for f in sf.fields[1:]] print(f 字段: {field_names}) sf.close() except Exception as e: print(f {base} 讀取失敗: {e})這段代碼的核心是調(diào)用pyshp的Reader對(duì)象shapeType返回的是幾何類型代碼3代表PolyLine即線要素5代表Polygon即面要素1代表Point即點(diǎn)要素。拿到字段列表后你就能判斷哪個(gè)字段適合做名稱標(biāo)注、哪個(gè)字段是道路等級(jí)代碼、哪個(gè)字段可以用來做分類渲染。參數(shù)上唯一需要改的是data_dir路徑如果你的shp文件和解壓后的文件夾放在一起也可以直接指到根目錄。3. 坐標(biāo)系與屬性表編碼打開前的兩個(gè)關(guān)鍵設(shè)置3.1 先答三個(gè)問題坐標(biāo)系、投影、單位加載shp數(shù)據(jù)后地圖上的位置不對(duì)十有八九是坐標(biāo)系問題。中國的基礎(chǔ)地理數(shù)據(jù)最常用的有三種坐標(biāo)系WGS84GPS原生坐標(biāo)、CGCS2000國家大地坐標(biāo)系、Xian 80老圖常用。前兩者的經(jīng)緯度數(shù)值幾乎看不出差別但平面投影后的坐標(biāo)值完全不同。怎么判斷你的這套數(shù)據(jù)是什么坐標(biāo)系先看有沒有.prj文件有的話用文本編輯器直接打開里面寫著完整的坐標(biāo)系統(tǒng)定義。如果能看到GCS_China_Geographic_Coordinate_System_2000或者CGCS2000字樣那就是CGCS2000如果只有GCS_WGS_1984就是WGS84。沒有.prj的情況下加載到QGIS后看鼠標(biāo)指針顯示的地圖坐標(biāo)值——經(jīng)緯度范圍在90到135之間說明是地理坐標(biāo)系如果是帶米單位的數(shù)值說明用了投影坐標(biāo)系比如高斯-克呂格投影。這里要特別提醒用投影坐標(biāo)系還是地理坐標(biāo)系直接決定長度和面積計(jì)算準(zhǔn)不準(zhǔn)。你如果只做展示、配色地理坐標(biāo)無所謂但要做道路里程統(tǒng)計(jì)、面積計(jì)算、緩沖區(qū)分析就必須先投影到合適的米制坐標(biāo)系。甘肅地域跨度大常用的投影帶比較分散不能簡單套一個(gè)全國范圍投影。3.2 屬性表亂碼dbf編碼與修復(fù)入口中文屬性表亂碼是這套數(shù)據(jù)最高頻的翻車現(xiàn)場。.dbf是dBASE時(shí)代的格式默認(rèn)用系統(tǒng)代碼頁編碼簡體中文Windows下通常表現(xiàn)為CP936也就是GBK。但Mac或Linux下打開很多GIS軟件默認(rèn)按UTF-8解碼于是“甘肅省”就變成了“鐢樿們鐪?”看著一團(tuán)亂。在QGIS里修復(fù)亂碼不需要重裝數(shù)據(jù)右鍵圖層選擇“屬性”切到“數(shù)據(jù)源”選項(xiàng)卡在“圖層編碼”里把默認(rèn)的UTF-8改成GBK或CP936屬性表立刻恢復(fù)可讀。ArcGIS Pro通常自動(dòng)識(shí)別但如果你在ArcMap里遇到亂碼可以去“ArcMap選項(xiàng)→表格→編碼默認(rèn)值”里調(diào)整。還有一種一勞永逸的辦法用ogr2ogr把dbf重新轉(zhuǎn)一遍編碼讓數(shù)據(jù)本身變成UTF-8ogr2ogr -lco ENCODINGUTF-8 甘肅基礎(chǔ)數(shù)據(jù)_utf8/ 甘肅基礎(chǔ)數(shù)據(jù)/ *.shp這段命令會(huì)把整個(gè)目錄下的shp重新寫出到一個(gè)新文件夾并把屬性表編碼強(qiáng)制設(shè)為UTF-8。-lco ENCODINGUTF-8是創(chuàng)建新數(shù)據(jù)時(shí)的編碼控制選項(xiàng)一定要寫否則ogr2ogr可能沿用源文件的編碼聲明。轉(zhuǎn)換完成后在QGIS里重新加載屬性表中文就能正常顯示。3.3 缺少.prj文件時(shí)怎么估算坐標(biāo)系沒有.prj的文件坐標(biāo)系是個(gè)黑匣子。我的排查方法是先用QGIS加載取消“CRS transformation”提示讓圖層落在WGS84的默認(rèn)范圍然后看幾何體的大致位置。如果點(diǎn)的經(jīng)緯度落在東經(jīng)90110、北緯3045之間說明是甘肅一帶的地理坐標(biāo)如果坐標(biāo)是五六百萬的大數(shù)字比如X18500000說明是高斯-克呂格投影后的米制坐標(biāo)這類數(shù)據(jù)通常需要根據(jù)帶號(hào)反推投影分帶。更省力的做法是寫段Python讀取.prj并解析出EPSG編碼直接把坐標(biāo)系定死from pyproj import CRS with open(鄉(xiāng)鎮(zhèn)名.prj, encodingutf-8, errorsignore) as f: wkt_text f.read() crs CRS.from_wkt(wkt_text) print(坐標(biāo)系名稱:, crs.name) try: print(EPSG編碼:, crs.to_epsg()) except Exception: print(EPSG編碼: 未匹配可能需要手動(dòng)指定) for axis in crs.axis_info: if axis.abbrev not in (None, ): print(f軸 {axis.name}: 單位 {axis.unit_name} ({axis.unit_abbrev}))這段代碼里CRS.from_wkt把.prj文本解析成坐標(biāo)系對(duì)象crs.name輸出可讀名稱to_epsg()嘗試返回權(quán)威編碼。注意axis_info是復(fù)數(shù)包含x、y兩個(gè)軸輸出單位能看出是“degree”還是“metre”。參數(shù)上如果直接讀中文.prj文件errorsignore能避免微軟的BOM頭干擾解碼。4. 把shp用起來QGIS加載、屬性篩選與GeoJSON導(dǎo)出4.1 QGIS加載拖拽、編碼與首屏校驗(yàn)加載shp最無腦的方式是直接把文件拖進(jìn)QGIS窗口但拖進(jìn)去之后不要馬上開始畫圖先做三件事確認(rèn)圖層編碼、確認(rèn)坐標(biāo)系、確認(rèn)幾何類型正確。點(diǎn)擊圖層面板左下角的“屬性”按鈕檢查“數(shù)據(jù)源”選項(xiàng)卡里的編碼設(shè)置把亂碼的圖層編碼改成GBK再切到“信息”選項(xiàng)卡看坐標(biāo)系是不是你預(yù)期的那個(gè)。首屏校驗(yàn)最直接把“鄉(xiāng)鎮(zhèn)名”圖層開出來和底圖疊加縮放后看點(diǎn)是否落在甘肅范圍內(nèi)。如果所有要素集中在某個(gè)角上或者跑到國外去了說明坐標(biāo)系不匹配QGIS的“CRS Transformation”通常會(huì)自動(dòng)處理但遇到同名圖層文件時(shí)偶爾會(huì)彈窗選來源坐標(biāo)選錯(cuò)就會(huì)出現(xiàn)位置偏移。4.2 屬性篩選用SQL表達(dá)式選出國道和鄉(xiāng)鎮(zhèn)名基礎(chǔ)數(shù)據(jù)真正好用之處在于屬性篩選。比如你只想保留“國道”圖層中的國道線路不需要省道和縣鄉(xiāng)道同時(shí)顯示可以右鍵圖層→“過濾”。QGIS的表達(dá)式語法用起來像SQL很適合做條件篩眩-- 用字段篩選把國道單獨(dú)拿出來 class 國道如果字段名是漢字比如“道路等級(jí)”需要在QGIS表達(dá)式對(duì)話框中點(diǎn)擊字段列表自動(dòng)插入注意字段名要用雙引號(hào)包起來字符串值要用單引號(hào)。篩選成功后圖層面板里該圖層會(huì)顯示一個(gè)漏斗圖標(biāo)數(shù)據(jù)只顯示符合條件的那部分?!班l(xiāng)鎮(zhèn)名”圖層也類似可以用鄉(xiāng)鎮(zhèn)名 LIKE %城關(guān)%這樣的模糊匹配把包含特定關(guān)鍵詞的鄉(xiāng)鎮(zhèn)挑出來。4.3 導(dǎo)出GeoJSON過濾字段與坐標(biāo)系轉(zhuǎn)換做完篩選后不是直接截圖交差而是導(dǎo)出成對(duì)方能用的格式。Web端好兄弟更認(rèn)GeoJSONPostGIS入庫則用SQL直接導(dǎo)入。用ogr2ogr導(dǎo)出國道圖層是一個(gè)穩(wěn)妥方案ogr2ogr -f GeoJSON 國道_output.geojson \ 甘肅基礎(chǔ)數(shù)據(jù)/國道.shp \ -where class 國道 \ -t_srs EPSG:4326-where參數(shù)把屬性篩選條件寫進(jìn)導(dǎo)出過程只導(dǎo)出符合條件的要素-t_srs EPSG:4326強(qiáng)制輸出到WGS84經(jīng)緯度坐標(biāo)。如果你的原數(shù)據(jù)已經(jīng)是地理坐標(biāo)系這一步可省略但如果原數(shù)據(jù)是投影坐標(biāo)這個(gè)參數(shù)能把數(shù)據(jù)轉(zhuǎn)成經(jīng)緯度方便在Leaflet、Mapbox這類前端地圖庫中直接疊加。導(dǎo)出之前建議先用第3章的腳本確認(rèn)源坐標(biāo)系避免轉(zhuǎn)換偏移。4.4 Python直讀geopandas批量導(dǎo)出中心文件如果你要的不是單層導(dǎo)出而是把整包shp按統(tǒng)一標(biāo)準(zhǔn)轉(zhuǎn)成GeoJSON建議用geopandas寫批量腳本import geopandas as gpd import os src_dir 甘肅基礎(chǔ)數(shù)據(jù)/shp out_dir 甘肅基礎(chǔ)數(shù)據(jù)/geojson os.makedirs(out_dir, exist_okTrue) for fn in os.listdir(src_dir): if fn.endswith(.shp): gdf gpd.read_file(os.path.join(src_dir, fn)) # 統(tǒng)一轉(zhuǎn)成 WGS84 gdf gdf.to_crs(epsg4326) out_path os.path.join(out_dir, fn.replace(.shp, .geojson)) gdf.to_file(out_path, driverGeoJSON, encodingutf-8) print(f已導(dǎo)出: {out_path}, {len(gdf)} 條)gpd.read_file直接讀shpto_crs(epsg4326)把坐標(biāo)系統(tǒng)一轉(zhuǎn)成經(jīng)緯度to_file寫出GeoJSON。encodingutf-8這個(gè)參數(shù)在QGIS里也對(duì)應(yīng)著圖層編碼導(dǎo)出后打開前端地圖屬性表不再亂碼。這套腳本最大的價(jià)值是可以一次性轉(zhuǎn)完整包不用在軟件里一層層點(diǎn)。5. 避坑記錄五個(gè)高頻翻車現(xiàn)場5.1 屬性表全是問號(hào)中文名碎成一地現(xiàn)象加載后在屬性表里看到“????”或者“鍖椾含”之類的亂碼無法讀取鄉(xiāng)鎮(zhèn)名和道路名稱。原因dbf文件是GBK編碼而QGIS默認(rèn)按UTF-8解碼兩側(cè)編碼不一致。解決在圖層屬性→數(shù)據(jù)源→圖層編碼里手動(dòng)改成GBK或CP936。如果多處圖層都這樣就按第3.2節(jié)的ogr2ogr命令整批轉(zhuǎn)成UTF-8之后所有圖層統(tǒng)一用UTF-8加載。5.2 QGIS提示Invalid Data Source圖層拖進(jìn)去就是加載失敗現(xiàn)象把shp拖進(jìn)去后QGIS報(bào)“Invalid Data Source”圖層面板里沒有出現(xiàn)圖層。原因多數(shù)情況是.shp文件缺失伴隨的.dbf或.shx也有可能是目錄路徑包含中文或空格導(dǎo)致底層驅(qū)動(dòng)找不到文件。解決先看壓縮包里的同名文件是否齊全至少.shp、.dbf、.shx要在同一目錄。其次把路徑改成純英文比如D:/gansu_data/shp/避免沿途目錄里有中文。5.3 圖層出現(xiàn)但所有要素跑到地圖范圍外縮放到圖層也看不見現(xiàn)象加載后地圖上什么也沒有“縮放到圖層”看到的是一片空白或者幾何越界。原因原始shp沒有.prj文件QGIS默認(rèn)按WGS84經(jīng)緯度解析但數(shù)據(jù)實(shí)際是投影坐標(biāo)系米制值這種情況下坐標(biāo)值被當(dāng)成經(jīng)緯度用位置偏移到幾百萬度之外。解決按3.3節(jié)的方法讀取或估算坐標(biāo)系然后在QGIS圖層屬性里手動(dòng)指定正確的坐標(biāo)系讓QGIS重新投影。5.4 篩選“國道”篩選不出任何東西現(xiàn)象寫了class 國道篩完結(jié)果為零但圖層明明有國道。原因字段類型不匹配。如果字段是數(shù)值型比如道路等級(jí)用1、2、3表示而你拿文本“國道”去比自然永遠(yuǎn)匹配不上。恰恰相反如果是文本字段但值包含空格或者全角字符也匹配不了。解決先在屬性表里查看該字段的具體值和類型。數(shù)值字段改用class 1文本字段檢查內(nèi)容里是否有空格用trim()函數(shù)包裹字段再比較。5.5 編輯要素后無法保存提示文件被鎖定現(xiàn)象改了某個(gè)圖層的屬性點(diǎn)保存的時(shí)候提示“could not remove file”或者“permission denied”。原因shp文件的鎖定通常是兩個(gè)一是QGIS里正在開著該圖層的編輯會(huì)話二是文件被另一個(gè)GIS軟件或殺毒軟件占用。解決確認(rèn)關(guān)閉編輯會(huì)話、關(guān)閉所有引用該文件的窗口后重試。路徑在U盤或共享盤的話把文件復(fù)制到本地再編輯。這個(gè)坑幾乎每個(gè)做GIS的人都會(huì)踩一次我的習(xí)慣是編輯前先復(fù)制一份備份改壞了隨手可以把原始文件拖回來。6. 進(jìn)階數(shù)據(jù)體檢與拓?fù)湫迯?fù)基礎(chǔ)數(shù)據(jù)里的矢量圖層不是拿來就能直接用于分析的尤其是道路和水系這類線要素經(jīng)常存在懸掛線、重復(fù)線段、自相交之類的問題。拿來做緩沖區(qū)分析或者網(wǎng)絡(luò)分析之前先跑一遍拓?fù)錂z查能省很多事。QGIS里裝Topology Checker插件按下面步驟操作插件→拓?fù)錂z查器點(diǎn)擊“配置”選擇要檢查的圖層。規(guī)則選擇“不能有懸掛線”對(duì)應(yīng)線要素的端點(diǎn)必須與其它線相交或落在端點(diǎn)范圍內(nèi)。運(yùn)行檢查后不符合規(guī)則的線會(huì)高亮出來縮放到錯(cuò)誤位置查看原因。對(duì)于重疊面或者縫隙規(guī)則用“不能有重疊”和“不能有縫隙”。拓?fù)溴e(cuò)誤修復(fù)的手段比較粗糙也有效手動(dòng)移動(dòng)頂點(diǎn)、刪除重復(fù)要素、用v.clean破面修復(fù)。我不建議在基礎(chǔ)數(shù)據(jù)里直接改原圖而是先用“保存為”→GeoPackage導(dǎo)出當(dāng)前圖層把原shp當(dāng)參考改的是副本。數(shù)據(jù)體檢還包括另一個(gè)習(xí)慣拿到數(shù)據(jù)先看要素計(jì)數(shù)記錄每個(gè)圖層原始記錄數(shù)再做任何刪除操作前再次對(duì)比數(shù)量。這個(gè)習(xí)慣救過我多次——有一次我在QGIS里做道路篩選明明只選中五百條保存后整個(gè)圖層剩三十條如果不看記錄數(shù)就會(huì)把原始數(shù)據(jù)毀了。從那以后我每次拿到新的shp資源都會(huì)強(qiáng)制走一遍“編碼檢查→坐標(biāo)系復(fù)核→記錄數(shù)快照→副本備份”的流程再開始做任何處理。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取