Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Bài 15: Phân tích Dữ liệu vector với GeoPandas

GeoPandas là thư viện mạnh mẽ nhất cho phân tích dữ liệu địa không gian trong Python, kết hợp sức mạnh của pandas và Shapely để mang đến trải nghiệm xử lý dữ liệu GIS hoàn hảo.

15.1. Mục tiêu học tập

Sau khi hoàn thành bài học này, bạn sẽ có thể:

  1. Tạo và manipulate GeoDataFrames từ các nguồn dữ liệu đa dạng

  2. Đọc và ghi dữ liệu địa không gian ở nhiều format khác nhau (Shapefile, GeoJSON, etc.)

  3. Làm chủ hệ tọa độ (CRS) và thực hiện chuyển đổi projection chính xác

  4. Tạo visualizations và maps chuyên nghiệp với matplotlib integration

  5. Thực hiện spatial operations phức tạp như joins, overlays, và buffer operations

  6. Tích hợp GeoPandas với các thư viện GIS khác trong ecosystem Python

# Import các thư viện chính
import geopandas as gpd           # Thư viện chính cho phân tích địa không gian
import pandas as pd                # Xử lý dữ liệu bảng
from shapely.geometry import Point, Polygon
import os 
import warnings                  # Quản lý cảnh báo
warnings.filterwarnings('ignore')  # Ẩn cảnh báo không cần thiết
outpath = r'G:\My Drive\python\geocourse\data\vector'

15.2 Tạo và hiểu GeoDataFrames

GeoDataFrame là pandas DataFrame đặc biệt có cột geometry chứa các đối tượng hình học Shapely. Đây là nền tảng của mọi phân tích địa không gian trong GeoPandas. Cũng giống như pandas, geopandas có hai loại object chính là GeoseriesGeoDataFrame.

15.2.1. Tạo GeoSeries

GeoSeries là một cột duy nhất chứa geometry với hệ tọa độ (CRS)

geo_series = gpd.GeoSeries([
    Point(105.85, 21.02),  # Hà Nội
    Point(106.63, 10.77),  # TP.HCM
], crs='EPSG:4326')
print(f"Kiểu dữ liệu GeoSeries:\n{geo_series}\n")
Kiểu dữ liệu GeoSeries:
0    POINT (105.85 21.02)
1    POINT (106.63 10.77)
dtype: geometry

15.2.2. Tạo GeoDataFrame từ dictionary

# Dữ liệu thành phố lớn Việt Nam (tọa độ mang tính tương đối). Dữ liệu này chỉ mang tính minh họa và nên được kiểm tra lại với dữ liệu chính thức.
vietnam_cities_data = {
    'city': ['Hà Nội', 'TP.HCM', 'Hải Phòng', 'Đà Nẵng', 'Cần Thơ', 'Biên Hòa', 'Huế', 'Nha Trang', 'Buôn Ma Thuột', 'Quy Nhon'],
    'province': ['Hà Nội', 'TP.HCM', 'Hải Phòng', 'Đà Nẵng', 'Cần Thơ', 'Đồng Nai', 'Thừa Thiên Huế', 'Khánh Hòa', 'Đắk Lắk', 'Bình Định'],
    'region': ['Miền Bắc', 'Miền Nam', 'Miền Bắc', 'Miền Trung', 'Miền Nam', 'Miền Nam', 'Miền Trung', 'Miền Trung', 'Miền Trung', 'Miền Trung'],
    'population': [8246600, 8993082, 2028514, 1134310, 1282937, 1104800, 455230, 423000, 340000, 284000],
    'longitude': [105.8542, 106.6297, 106.6881, 108.2022, 105.7469, 106.8439, 107.5905, 109.1967, 108.0373, 109.2189],
    'latitude': [21.0285, 10.8231, 20.8449, 16.0544, 10.0452, 10.9460, 16.4637, 12.2585, 12.6667, 13.7830],
    'is_port_city': [False, True, True, True, True, False, False, True, False, True],
}

# Tạo DataFrame thường trước
df = pd.DataFrame(vietnam_cities_data)
# tạo cột geometry từ longitude và latitude
geometry = [Point(xy) for xy in zip(df.longitude, df.latitude)]
# Tạo GeoDataFrame từ DataFrame và cột geometry
gdf = gpd.GeoDataFrame(
    df.drop(['longitude', 'latitude'], axis=1), 
    geometry=geometry, 
    crs='EPSG:4326'
)
gdf.head()
cityprovinceregionpopulationis_port_citygeometry
0Hà NộiHà NộiMiền Bắc8246600FalsePOINT (105.8542 21.0285)
1TP.HCMTP.HCMMiền Nam8993082TruePOINT (106.6297 10.8231)
2Hải PhòngHải PhòngMiền Bắc2028514TruePOINT (106.6881 20.8449)
3Đà NẵngĐà NẵngMiền Trung1134310TruePOINT (108.2022 16.0544)
4Cần ThơCần ThơMiền Nam1282937TruePOINT (105.7469 10.0452)

15.2.3. Tạo GeoDataFrame từ list

# Tạo GeoDataFrames từ polygons sử dụng shapely và gán vào geometry
# Tạo một số polygons ví dụ
geometry = [
    Polygon([(105, 20), (106, 20), (106, 21), (105, 21)]),
    Polygon([(106, 10), (107, 10), (107, 11), (106, 11)]),
    Polygon([(108, 16), (109, 16), (109, 17), (108, 17)]),
    Polygon([(109, 12), (110, 12), (110, 13), (109, 13)]),
    Polygon([(105, 10), (106, 10), (106, 11), (105, 11)]),
    Polygon([(106, 10), (107, 10), (107, 11), (106, 11)]),
    Polygon([(107, 16), (108, 16), (108, 17), (107, 17)]),
    Polygon([(109, 12), (110, 12), (110, 13), (109, 13)]),
    Polygon([(108, 12), (109, 12), (109, 13), (108, 13)]),
    Polygon([(109, 13), (110, 13), (110, 14), (109, 14)]),
]
polygon = gpd.GeoDataFrame(
    geometry=geometry,
    crs='EPSG:4326'
)
# Ta có thêm cột thuộc tính vào GeoDataFrame polygon
polygon["landcover"] = ["Urban", "Urban", "Rural", "Rural", "Urban", "Urban", "Rural", "Rural", "Rural", "Rural"]
polygon.head()
geometrylandcover
0POLYGON ((105 20, 106 20, 106 21, 105 21, 105 ...Urban
1POLYGON ((106 10, 107 10, 107 11, 106 11, 106 ...Urban
2POLYGON ((108 16, 109 16, 109 17, 108 17, 108 ...Rural
3POLYGON ((109 12, 110 12, 110 13, 109 13, 109 ...Rural
4POLYGON ((105 10, 106 10, 106 11, 105 11, 105 ...Urban

15.3. Đọc và Lưu Dữ liệu Địa không gian

GeoPandas có thể đọc và ghi hơn 20 định dạng địa không gian khác nhau. Đây là kỹ năng thiết yếu cho công việc thực tế.

Dữ liệu sử dụng trong notebook này được tải từ GADM và NDVI từ ảnh MODI.

15.3.1. Đọc dữ liệu

# Đọc dữ liệu từ local machine
districts = gpd.read_file(r'G:\My Drive\python\geocourse\data\vector\Vietnam_districts.geojson')
# Chuyển crs từ 4326 sang 32648 (UTM 48N)
districts = districts.to_crs(epsg=32648)
# Thêm cột diện tích 
districts['area_km2'] = districts.geometry.area/1e6  # Chuyển từ m2 sang km2
districts.head()
codesNAME_1NAME_2NDVIgeometryarea_km2
0A01AnGiangAnPhú0.571832MULTIPOLYGON (((517117.408 1197043.213, 517502...226.849284
1A02AnGiangChâuĐốc0.596210MULTIPOLYGON (((511484.367 1175988.508, 508914...103.388808
2A03AnGiangChâuPhú0.583617MULTIPOLYGON (((520563.388 1156311.715, 519676...450.432027
3A04AnGiangChâuThành0.605679MULTIPOLYGON (((540603.418 1145482.678, 540308...354.959119
4A05AnGiangChợMới0.607893MULTIPOLYGON (((553305.107 1165103.948, 553435...368.509538
# Đọc dữ liệu từ url 
vietnam_data = gpd.read_file('https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_VNM_1.json')
vietnam_data.head()
GID_1GID_0COUNTRYNAME_1VARNAME_1NL_NAME_1TYPE_1ENGTYPE_1CC_1HASC_1ISO_1geometry
0VNM.1_1VNMVietnamAnGiangAnGiangNATỉnhProvinceNAVN.AGVN-44MULTIPOLYGON (((105.5486 10.4295, 105.5495 10....
1VNM.7_1VNMVietnamBàRịa-VũngTàuBaRia-VungTauNATỉnhProvinceNAVN.BVNAMULTIPOLYGON (((107.0901 10.324, 107.0889 10.3...
2VNM.3_1VNMVietnamBắcGiangBacGiangNATỉnhProvinceNAVN.BGNAMULTIPOLYGON (((106.2838 21.1323, 106.2734 21....
3VNM.4_1VNMVietnamBắcKạnBacKanNATỉnhProvinceNAVN.BKNAMULTIPOLYGON (((105.8724 21.8558, 105.8629 21....
4VNM.2_1VNMVietnamBạcLiêuBacLieuNATỉnhProvinceNAVN.BLNAMULTIPOLYGON (((105.4244 9.0213, 105.4164 9.01...

15.3.2. Viết dữ liệu

# Lưu dữ liệu ra file GeoJSON
vietnam_data.to_file(os.path.join(outpath, 'vietnam_provinces.geojson'), driver='GeoJSON')
# Lưu dữ liệu ra file Shapefile
vietnam_data.to_file(os.path.join(outpath, 'Vietnam_provincess.shp'), driver='ESRI Shapefile')
# Lưu dữ liệu ra file Parquet
vietnam_data.to_file(os.path.join(outpath, 'Vietnam_provinces.parquet'), driver='Parquet')
# Lưu dữ liệu ra file GeoPackage
vietnam_data.to_file(os.path.join(outpath, 'Vietnam_provinces.gpkg'), driver='GPKG')

15.4. Thao tác và Phân tích Không gian

Đây là phần mạnh nhất của GeoPandas - các thao tác không gian cho phép phân tích mối quan hệ địa lý phức tạp.

# Đọc dữ liệu từ url 
province = gpd.read_file('https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_VNM_1.json')
# Select two columns 
province = province[['VARNAME_1', 'geometry']]
# Rename the column
province = province.rename(columns={'VARNAME_1': 'province'})
province.head()
provincegeometry
0AnGiangMULTIPOLYGON (((105.5486 10.4295, 105.5495 10....
1BaRia-VungTauMULTIPOLYGON (((107.0901 10.324, 107.0889 10.3...
2BacGiangMULTIPOLYGON (((106.2838 21.1323, 106.2734 21....
3BacKanMULTIPOLYGON (((105.8724 21.8558, 105.8629 21....
4BacLieuMULTIPOLYGON (((105.4244 9.0213, 105.4164 9.01...

15.4.1. Tạo buffer

Trước khi tạo buffer, chúng ta nên chuyển dữ liệu qua hệ tọa độ UTM cho độ chính xác cao hơn.

# Chuyển hệ tọa độ từ WGS84 (EPSG:4326) sang UTM 48N (EPSG:32648)
province_utm = province.to_crs(epsg=32648)
# Chọn tỉnh vĩnh phúc
vinhphuc = province_utm[province_utm['province'] == 'VinhPhuc']
# Tạo 1km buffer quanh tỉnh vĩnh phúc
vinhphuc_buffer = vinhphuc.buffer(1000)  # Buffer 1000 mét (1 km)
# Chuyển từ GeoSeries sang GeoDataFrame để dễ dàng xử lý
vinhphuc_buffer_gdf = gpd.GeoDataFrame(geometry=vinhphuc_buffer, crs='EPSG:32648')
vinhphuc_buffer_gdf.head()
geometry
61POLYGON ((534128.869 2379245.172, 534157.041 2...

15.4.2. Sử dụng phép join giữa hai GeoDataFrame

# Đọc dữ liệu districts từ url
districts = gpd.read_file('https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_VNM_2.json')
# Đảm bảo dữ liệu vinhphuc có cùng hệ tọa độ với districts trước khi join
vinhphuc = vinhphuc.to_crs(districts.crs)
# Join dữ liệu tỉnh vĩnh phúc với dữ liệu districts để lấy ra các huyện thuộc tỉnh vĩnh phúc
vinhphuc_districts = gpd.sjoin(vinhphuc, districts, how='inner', predicate='intersects') # ngoài intersects còn có within, contains, touches, crosses, covers, covered_by.
vinhphuc_districts.head()
provincegeometryindex_rightGID_2GID_0COUNTRYGID_1NAME_1NL_NAME_1NAME_2VARNAME_2NL_NAME_2TYPE_2ENGTYPE_2CC_2HASC_2
61VinhPhucMULTIPOLYGON (((105.5713 21.1615, 105.5336 21....250VNM.27.23_1VNMVietnamVNM.27_1HàNộiNASócSơnSocSonNAHuyệnDistrictNAVN.NB.HL
61VinhPhucMULTIPOLYGON (((105.5713 21.1615, 105.5336 21....615VNM.56.4_1VNMVietnamVNM.56_1TháiNguyênNAPhổYênPhoYenNAThịxãTownNAVN.TV.TI
61VinhPhucMULTIPOLYGON (((105.5713 21.1615, 105.5336 21....230VNM.27.4_1VNMVietnamVNM.27_1HàNộiNABaVìBaViNAHuyệnDistrictNAVN.KG.HT
61VinhPhucMULTIPOLYGON (((105.5713 21.1615, 105.5336 21....251VNM.27.24_1VNMVietnamVNM.27_1HàNộiNASơnTâySonTayNAThịxãTownNAVN.TN.HT
61VinhPhucMULTIPOLYGON (((105.5713 21.1615, 105.5336 21....248VNM.27.21_1VNMVietnamVNM.27_1HàNộiNAPhúcThọPhucThoNAHuyệnDistrictNAVN.HO.HB

Tóm tắt

Bạn đã hoàn thành Bài 5 và học được GeoPandas - thư viện “con dao Thụy Sĩ” cho geospatial data analysis trong Python.

Các khái niệm chính đã nắm vững:

Kỹ năng bạn có thể áp dụng: