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ể:
Tạo và manipulate GeoDataFrames từ các nguồn dữ liệu đa dạng
Đọc và ghi dữ liệu địa không gian ở nhiều format khác nhau (Shapefile, GeoJSON, etc.)
Làm chủ hệ tọa độ (CRS) và thực hiện chuyển đổi projection chính xác
Tạo visualizations và maps chuyên nghiệp với matplotlib integration
Thực hiện spatial operations phức tạp như joins, overlays, và buffer operations
Tích hợp GeoPandas với các thư viện GIS khác trong ecosystem Python
Import thư viện cần thiết
# 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à Geoseries và GeoDataFrame.
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: geometry15.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()| city | province | region | population | is_port_city | geometry | |
|---|---|---|---|---|---|---|
| 0 | Hà Nội | Hà Nội | Miền Bắc | 8246600 | False | POINT (105.8542 21.0285) |
| 1 | TP.HCM | TP.HCM | Miền Nam | 8993082 | True | POINT (106.6297 10.8231) |
| 2 | Hải Phòng | Hải Phòng | Miền Bắc | 2028514 | True | POINT (106.6881 20.8449) |
| 3 | Đà Nẵng | Đà Nẵng | Miền Trung | 1134310 | True | POINT (108.2022 16.0544) |
| 4 | Cần Thơ | Cần Thơ | Miền Nam | 1282937 | True | POINT (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()| geometry | landcover | |
|---|---|---|
| 0 | POLYGON ((105 20, 106 20, 106 21, 105 21, 105 ... | Urban |
| 1 | POLYGON ((106 10, 107 10, 107 11, 106 11, 106 ... | Urban |
| 2 | POLYGON ((108 16, 109 16, 109 17, 108 17, 108 ... | Rural |
| 3 | POLYGON ((109 12, 110 12, 110 13, 109 13, 109 ... | Rural |
| 4 | POLYGON ((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 lưu trữ trên máy
# Đọ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()| codes | NAME_1 | NAME_2 | NDVI | geometry | area_km2 | |
|---|---|---|---|---|---|---|
| 0 | A01 | AnGiang | AnPhú | 0.571832 | MULTIPOLYGON (((517117.408 1197043.213, 517502... | 226.849284 |
| 1 | A02 | AnGiang | ChâuĐốc | 0.596210 | MULTIPOLYGON (((511484.367 1175988.508, 508914... | 103.388808 |
| 2 | A03 | AnGiang | ChâuPhú | 0.583617 | MULTIPOLYGON (((520563.388 1156311.715, 519676... | 450.432027 |
| 3 | A04 | AnGiang | ChâuThành | 0.605679 | MULTIPOLYGON (((540603.418 1145482.678, 540308... | 354.959119 |
| 4 | A05 | AnGiang | ChợMới | 0.607893 | MULTIPOLYGON (((553305.107 1165103.948, 553435... | 368.509538 |
Đọc dữ liệu từ
url
# Đọ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_1 | GID_0 | COUNTRY | NAME_1 | VARNAME_1 | NL_NAME_1 | TYPE_1 | ENGTYPE_1 | CC_1 | HASC_1 | ISO_1 | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | VNM.1_1 | VNM | Vietnam | AnGiang | AnGiang | NA | Tỉnh | Province | NA | VN.AG | VN-44 | MULTIPOLYGON (((105.5486 10.4295, 105.5495 10.... |
| 1 | VNM.7_1 | VNM | Vietnam | BàRịa-VũngTàu | BaRia-VungTau | NA | Tỉnh | Province | NA | VN.BV | NA | MULTIPOLYGON (((107.0901 10.324, 107.0889 10.3... |
| 2 | VNM.3_1 | VNM | Vietnam | BắcGiang | BacGiang | NA | Tỉnh | Province | NA | VN.BG | NA | MULTIPOLYGON (((106.2838 21.1323, 106.2734 21.... |
| 3 | VNM.4_1 | VNM | Vietnam | BắcKạn | BacKan | NA | Tỉnh | Province | NA | VN.BK | NA | MULTIPOLYGON (((105.8724 21.8558, 105.8629 21.... |
| 4 | VNM.2_1 | VNM | Vietnam | BạcLiêu | BacLieu | NA | Tỉnh | Province | NA | VN.BL | NA | MULTIPOLYGON (((105.4244 9.0213, 105.4164 9.01... |
15.3.2. Viết dữ liệu¶
Lưu dữ liệu ra file
GeoJSON
# 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
shapefile
# 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
Parquetfile
# 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
GeoPackage
# 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()| province | geometry | |
|---|---|---|
| 0 | AnGiang | MULTIPOLYGON (((105.5486 10.4295, 105.5495 10.... |
| 1 | BaRia-VungTau | MULTIPOLYGON (((107.0901 10.324, 107.0889 10.3... |
| 2 | BacGiang | MULTIPOLYGON (((106.2838 21.1323, 106.2734 21.... |
| 3 | BacKan | MULTIPOLYGON (((105.8724 21.8558, 105.8629 21.... |
| 4 | BacLieu | MULTIPOLYGON (((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 | |
|---|---|
| 61 | POLYGON ((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()| province | geometry | index_right | GID_2 | GID_0 | COUNTRY | GID_1 | NAME_1 | NL_NAME_1 | NAME_2 | VARNAME_2 | NL_NAME_2 | TYPE_2 | ENGTYPE_2 | CC_2 | HASC_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 61 | VinhPhuc | MULTIPOLYGON (((105.5713 21.1615, 105.5336 21.... | 250 | VNM.27.23_1 | VNM | Vietnam | VNM.27_1 | HàNội | NA | SócSơn | SocSon | NA | Huyện | District | NA | VN.NB.HL |
| 61 | VinhPhuc | MULTIPOLYGON (((105.5713 21.1615, 105.5336 21.... | 615 | VNM.56.4_1 | VNM | Vietnam | VNM.56_1 | TháiNguyên | NA | PhổYên | PhoYen | NA | Thịxã | Town | NA | VN.TV.TI |
| 61 | VinhPhuc | MULTIPOLYGON (((105.5713 21.1615, 105.5336 21.... | 230 | VNM.27.4_1 | VNM | Vietnam | VNM.27_1 | HàNội | NA | BaVì | BaVi | NA | Huyện | District | NA | VN.KG.HT |
| 61 | VinhPhuc | MULTIPOLYGON (((105.5713 21.1615, 105.5336 21.... | 251 | VNM.27.24_1 | VNM | Vietnam | VNM.27_1 | HàNội | NA | SơnTây | SonTay | NA | Thịxã | Town | NA | VN.TN.HT |
| 61 | VinhPhuc | MULTIPOLYGON (((105.5713 21.1615, 105.5336 21.... | 248 | VNM.27.21_1 | VNM | Vietnam | VNM.27_1 | HàNội | NA | PhúcThọ | PhucTho | NA | Huyện | District | NA | VN.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:¶
✅ GeoDataFrames: Pandas DataFrames với geometry column cho spatial data
✅ Spatial I/O: Đọc/ghi Shapefile, GeoJSON, GPKG với read_file() và to_file()
✅ CRS management: Coordinate reference systems với to_crs() transformations
✅ Spatial operations: buffer(), intersects(), within() cho geometric analysis
✅ Spatial joins: Kết hợp datasets dựa trên spatial relationships
Kỹ năng bạn có thể áp dụng:¶
Thực hiện comprehensive spatial analysis với pandas-like syntax
Xử lý và visualize complex geospatial datasets một cách hiệu quả
Tích hợp spatial data với business intelligence và data science workflows
Phát triển location-based applications và market analysis tools
Chuẩn bị expertise foundation cho advanced GIS development và spatial data science