geopandas-geospatial について
geopandas-geospatialは、地理ベクターデータを扱うPythonユーザー向けのGeoPandasスキルです。pandas風のデータ操作に加え、空間解析と可視化を扱います。
主な機能
- 01複数のベクターファイル形式の読み書き
- 02座標参照系の変換
- 03空間結合とオーバーレイ分析
- 04幾何属性の計算
- 05コロプレスマップの作成
活用シーン
- 01地域ごとのポイント数の集計
- 02施設周辺のバッファー作成
- 03複数の地理データソースの統合
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill geopandas-geospatialインストール後、Claude Code で/geopandas-geospatialを入力して読み込まれたか確認します。
詳細
GeoPandas による地理空間分析
概要
GeoPandas は、幾何型に対する空間操作を追加して pandas を拡張し、pandas の DataFrame と Shapely のジオメトリ、ファイル入出力用の Fiona を組み合わせます。使い慣れた pandas に似た API を使用して、地理空間ベクターデータ(点、線、ポリゴン)の読み込み、書き込み、操作、可視化ができます。
使用する場面
- 空間ファイル形式(Shapefile、GeoJSON、GeoPackage、Parquet)の読み込みと書き込み
- 地理データセット間の空間結合(ポリゴン内の点、最近傍検索)の実行
- オーバーレイ操作(交差、和、差分、クリッピング)の実行
- 幾何学的なプロパティ(面積、距離、バッファー、重心)の計算
- コロプレスマップやインタラクティブなウェブマップの作成
- 座標参照系間でのデータの再投影
- 属性による空間フィーチャーの集約(dissolve)
- ラスターデータの分析には、代わりに rasterio/xarray を使用してください
- 大規模な分散地理空間処理には、Dask-GeoPandas または Apache Sedona を検討してください
前提条件
pip install geopandas matplotlib
# Optional:
# pip install folium — interactive maps
# pip install mapclassify — classification schemes for choropleth
# pip install contextily — basemaps
# pip install pyarrow — faster I/O (2-4x speedup)
# pip install psycopg2 geoalchemy2 — PostGIS support
クイックスタート
import geopandas as gpd
# Read spatial data
gdf = gpd.read_file("data.geojson")
print(f"Shape: {gdf.shape}, CRS: {gdf.crs}")
print(f"Geometry types: {gdf.geometry.geom_type.unique()}")
# Reproject, compute area, save
gdf_proj = gdf.to_crs("EPSG:3857")
gdf_proj['area_m2'] = gdf_proj.geometry.area
gdf_proj.to_file("output.gpkg")
# Quick map
gdf.plot(column='population', legend=True, figsize=(10, 8))
コア API
1. データ入出力
import geopandas as gpd
# Read various formats
gdf = gpd.read_file("data.shp") # Shapefile
gdf = gpd.read_file("data.geojson") # GeoJSON
gdf = gpd.read_file("data.gpkg") # GeoPackage
gdf = gpd.read_file("data.gpkg", layer="roads") # Specific layer
# Filtered reading (load only needed data)
gdf = gpd.read_file("data.gpkg", bbox=(xmin, ymin, xmax, ymax))
gdf = gpd.read_file("data.gpkg", columns=["name", "geometry"])
gdf = gpd.read_file("data.gpkg", where="population > 10000")
# Arrow acceleration (2-4x faster)
gdf = gpd.read_file("data.gpkg", use_arrow=True)
# Parquet/Feather (columnar, fast, preserves CRS)
gdf = gpd.read_parquet("data.parquet")
gdf.to_parquet("output.parquet")
# PostGIS database
from sqlalchemy import create_engine
engine = create_engine("postgresql://user:pass@host/db")
gdf = gpd.read_postgis("SELECT * FROM parcels", con=engine, geom_col='geom')
gdf.to_postgis("output_table", con=engine)
# Write
gdf.to_file("output.gpkg") # GeoPackage (recommended)
gdf.to_file("output.shp") # Shapefile
gdf.to_file("output.geojson", driver="GeoJSON")
2. CRS の管理
# Check current CRS
print(gdf.crs) # e.g., EPSG:4326
print(gdf.crs.is_geographic) # True for lat/lon
print(gdf.crs.is_projected) # True for meters
# Reproject (transforms coordinates)
gdf_proj = gdf.to_crs("EPSG:3857") # Web Mercator
gdf_proj = gdf.to_crs(epsg=32633) # UTM zone 33N
# Set CRS (only when metadata missing, does NOT transform coordinates)
gdf = gdf.set_crs("EPSG:4326")
# Estimate appropriate UTM zone
utm_crs = gdf.estimate_utm_crs()
gdf_utm = gdf.to_crs(utm_crs)
一般的な EPSG コード:
| Code | Name | Use |
|---|---|---|
| 4326 | WGS 84 | GPS 座標、ウェブデータ |
| 3857 | Web Mercator | ウェブマッピング(Google/OSM タイル) |
| 326xx | UTM zones (N) | 面積/距離の計算 |
| 5070 | Albers Equal Area (US) | 面積を保持する米国の地図 |
3. 幾何操作
# Buffer (expand/erode geometry by distance)
buffered = gdf.geometry.buffer(100) # 100 units (meters if projected)
eroded = gdf.geometry.buffer(-50) # Negative = erosion
# Simplify (reduce complexity)
simplified = gdf.geometry.simplify(tolerance=10, preserve_topology=True)
# Centroid, convex hull, envelope
centroids = gdf.geometry.centroid
hulls = gdf.geometry.convex_hull
bounds = gdf.geometry.envelope
# Union all geometries
unified = gdf.geometry.union_all()
# Affine transformations
rotated = gdf.geometry.rotate(angle=45, origin='center')
scaled = gdf.geometry.scale(xfact=2.0, yfact=2.0)
translated = gdf.geometry.translate(xoff=100, yoff=50)
# Geometric properties
areas = gdf.geometry.area # Use projected CRS for accuracy
lengths = gdf.geometry.length # Perimeter for polygons
is_valid = gdf.geometry.is_valid # Validate geometry
total = gdf.geometry.total_bounds # [minx, miny, maxx, maxy]
4. 空間分析
# Spatial join (combine datasets by spatial relationship)
joined = gpd.sjoin(points_gdf, polygons_gdf, predicate='intersects')
joined = gpd.sjoin(gdf1, gdf2, predicate='within')
joined = gpd.sjoin(gdf1, gdf2, predicate='contains', how='left')
# Nearest neighbor join
nearest = gpd.sjoin_nearest(gdf1, gdf2, max_distance=1000, distance_col='dist')
# Overlay operations (set-theoretic)
intersection = gpd.overlay(gdf1, gdf2, how='intersection')
union = gpd.overlay(gdf1, gdf2, how='union')
difference = gpd.overlay(gdf1, gdf2, how='difference')
sym_diff = gpd.overlay(gdf1, gdf2, how='symmetric_difference')
# Dissolve (aggregate by attribute)
dissolved = gdf.dissolve(by='region', aggfunc='sum')
dissolved = gdf.dissolve(by='region', aggfunc={'population': 'sum', 'area': 'mean'})
# Clip to boundary
clipped = gpd.clip(gdf, boundary_gdf)
# Distance calculations (use projected CRS)
distances = gdf.geometry.distance(single_point)
# Spatial predicates
within_mask = gdf1.geometry.within(gdf2.geometry)
intersects_mask = gdf1.geometry.intersects(gdf2.geometry)
5. 可視化
import matplotlib.pyplot as plt
# Basic plot
gdf.plot(figsize=(10, 8))
# Choropleth map
gdf.plot(column='population', cmap='YlOrRd', legend=True, figsize=(12, 8))
# Classification schemes (requires mapclassify)
gdf.plot(column='population', scheme='quantiles', k=5, legend=True)
gdf.plot(column='population', scheme='fisher_jenks', k=5, legend=True)
# Multi-layer map
fig, ax = plt.subplots(figsize=(12, 10))
polygons_gdf.plot(ax=ax, color='lightblue', edgecolor='black')
points_gdf.plot(ax=ax, color='red', markersize=10)
roads_gdf.plot(ax=ax, color='gray', linewidth=0.5)
ax.set_title('Multi-layer Map')
ax.set_axis_off()
# Interactive map (requires folium)
m = gdf.explore(column='population', cmap='YlOrRd', legend=True,
tooltip=['name', 'population'])
m.save('map.html')
# Multi-layer interactive
m = gdf1.explore(color='blue', name='Layer 1')
gdf2.explore(m=m, color='red', name='Layer 2')
import folium
folium.LayerControl().add_to(m)
# Basemap (requires contextily)
import contextily as ctx
gdf_wm = gdf.to_crs(epsg=3857)
ax = gdf_wm.plot(alpha=0.5, figsize=(10, 10))
ctx.add_basemap(ax)
主要な概念
データ構造
- GeoSeries:空間メソッド(面積、距離、バッファーなど)を備えた、Shapely ジオメトリの Pandas Series です
- GeoDataFrame:1 つ以上のジオメトリ列を持つ Pandas DataFrame です。列のうち 1 つが、空間メソッドで使用される「アクティブジオメトリ」です
from shapely.geometry import Point
# Create from coordinates
gdf = gpd.GeoDataFrame(
{'name': ['A', 'B'], 'value': [10, 20]},
geometry=[Point(0, 0), Point(1, 1)],
crs="EPSG:4326"
)
# Multiple geometry columns
gdf['centroid'] = gdf.geometry.centroid
gdf = gdf.set_geometry('centroid') # Switch active geometry
空間操作における CRS のルール
- 空間操作の前には、必ず CRS を確認してください:
print(gdf.crs) - 空間結合、オーバーレイ、または距離計算の前に CRS を一致させてください
- 面積/距離の計算には投影 CRS(メートル)を使用してください。地理 CRS(度)では誤った結果になります
- set_crs() はメタデータを追加するだけですが、to_crs() は座標を変換します
空間インデックス
GeoPandas は、sjoin、overlay、その他の空間操作用に空間インデックス(R-tree)を自動作成します。手動でクエリを実行する場合:
sindex = gdf.sindex
possible_idx = list(sindex.intersection((xmin, ymin, xmax, ymax)))
一般的なワークフロー
ワークフロー 1:読み込み → 変換 → 分析 → エクスポート
import geopandas as gpd
# Load
gdf = gpd.read_file("parcels.shp")
print(f"CRS: {gdf.crs}, Rows: {len(gdf)}")
# Transform to projected CRS for measurements
gdf = gdf.to_crs(gdf.estimate_utm_crs())
# Analyze
gdf['area_ha'] = gdf.geometry.area / 10000 # hectares
gdf['perimeter_m'] = gdf.geometry.length
print(f"Total area: {gdf['area_ha'].sum():.1f} ha")
# Export
gdf.to_file("parcels_analyzed.gpkg")
ワークフロー 2:空間結合と集約
# Count points per polygon
points_in_poly = gpd.sjoin(points_gdf, polygons_gdf, predicate='within')
counts = points_in_poly.groupby('index_right').agg(
point_count=('geometry', 'size'),
total_value=('value', 'sum')
)
result = polygons_gdf.merge(counts, left_index=True, right_index=True, how='left')
result['point_count'] = result['point_count'].fillna(0)
print(f"Polygons with points: {(result['point_count'] > 0).sum()}/{len(result)}")
ワークフロー 3:複数ソースの統合
# Read from different sources, ensure matching CRS
roads = gpd.read_file("roads.shp")
buildings = gpd.read_file("buildings.geojson")
parcels = gpd.read_postgis("SELECT * FROM parcels", con=engine, geom_col='geom')
target_crs = roads.crs
buildings = buildings.to_crs(target_crs)
parcels = parcels.to_crs(target_crs)
# Find buildings within 50m of roads
buildings_near_roads = gpd.sjoin_nearest(
buildings, roads, max_distance=50, distance_col='road_dist'
)
print(f"Buildings near roads: {len(buildings_near_roads)}/{len(buildings)}")
主要なパラメーター
| Parameter | Function | Default | Effect |
|---|---|---|---|
predicate | sjoin | 'intersects' | 空間的な関係:intersects、within、contains、touches、crosses |
how | sjoin, overlay | 'inner' | 結合の種類:inner、left、right |
max_distance | sjoin_nearest | None | 検索半径の上限(パフォーマンスが向上します) |
k | sjoin_nearest | 1 | 検索する最近傍の数 |
tolerance | simplify | Required | Douglas-Peucker 許容値(CRS の単位) |
preserve_topology | simplify | True | 自己交差を防止します |
resolution | buffer | 16 | バッファー曲線のセグメント数 |
aggfunc | dissolve | 'first' | ジオメトリ以外の列に適用する集約関数 |
use_arrow | read_file | False | Arrow による高速化を有効にします(2~4 倍高速) |
scheme | plot | None | 分類方法:quantiles、equal_interval、fisher_jenks |
k | plot (with scheme) | 5 | 分類区分の数 |
ベストプラクティス
- 空間演算を行う前に、必ず CRS を確認してください。CRS が一致していないと、誤った結果や空の結果になります。
- 面積や距離の計算には、投影 CRS を使用してください。地理座標 CRS(度)は、計測には意味がありません。
- 結合前に CRS を一致させてください。
gpd.sjoin(gdf1, gdf2)の前にgdf2 = gdf2.to_crs(gdf1.crs)を実行します。 - 複雑な演算の前に
.is_validでジオメトリを検証してください。無効なジオメトリは、気づきにくいエラーの原因になります。 - Shapefile より GeoPackage を使用してください。10 文字の列名制限がなく、複数のレイヤーに対応し、パフォーマンスも優れています。
- 読み込み時にフィルタリングしてください。大きなファイルでは
bbox、columns、whereを使用して、必要なデータだけを読み込みます。 sjoin_nearestではmax_distanceを設定してください。距離を制限しない最近傍検索は、大規模なデータセットでは時間がかかります。- ジオメトリを変更するときは
.copy()を使用してください。元の GeoDataFrame に意図しない影響が及ぶのを防げます。
よく使うレシピ
レシピ:ポリゴン内のポイントを数える
使用場面:地域(郡、流域)ごとにポイント観測(サンプル地点、観測値)を集計します。
import geopandas as gpd
regions = gpd.read_file("regions.geojson")
points = gpd.read_file("observations.geojson").to_crs(regions.crs)
joined = gpd.sjoin(points, regions, how="inner", predicate="within")
counts = joined.groupby("index_right").size().rename("point_count")
regions_with_counts = regions.join(counts).fillna(0)
regions_with_counts["point_count"] = regions_with_counts["point_count"].astype(int)
print(regions_with_counts[["name", "point_count"]].sort_values("point_count", ascending=False).head())
レシピ:ポイントの周囲にバッファーを作成し、重複を統合する
使用場面:ポイント位置からサービスエリアや集水域を作成します。
import geopandas as gpd
facilities = gpd.read_file("facilities.geojson")
utm_crs = facilities.estimate_utm_crs() # Project to meters
facilities_m = facilities.to_crs(utm_crs)
# 1 km buffer
buffers = facilities_m.copy()
buffers["geometry"] = facilities_m.geometry.buffer(1000)
# Dissolve overlapping buffers into single polygon
service_area = buffers.dissolve()
service_area_wgs84 = service_area.to_crs("EPSG:4326")
service_area_wgs84.to_file("service_area.geojson", driver="GeoJSON")
print(f"Service area: {service_area_wgs84.geometry.area.sum():.0f} sq degrees")
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| 空間結合の結果が空 | GeoDataFrame 間で CRS が一致していない | CRS が一致していることを確認します:gdf2 = gdf2.to_crs(gdf1.crs) |
| 面積や距離の値が誤っている | 地理座標 CRS(度)を使用している | 投影 CRS に再投影します:gdf.to_crs(gdf.estimate_utm_crs()) |
読み込み時に DriverError が発生する | ドライバーがない、またはファイルが破損している | ファイルが存在することを確認し、driver="GeoJSON" を明示的に指定して試します |
| マージ後にジオメトリ列が失われる | gdf.merge(df) ではなく df.merge(gdf) を呼び出している | 必ず GeoDataFrame に対して merge を呼び出します |
オーバーレイ時に TopologicalError が発生する | 無効なジオメトリ | gdf.geometry = gdf.geometry.buffer(0) で修正します |
sjoin_nearest が遅い | 大規模なデータセットで距離制限がない | max_distance パラメーターを設定します |
| Folium の地図が空白になる | ジオメトリが EPSG:4326 ではない | WGS84 に再投影します:gdf.to_crs(epsg=4326).explore() |
| Shapefile の列名が切り詰められる | Shapefile の 10 文字制限 | 代わりに GeoPackage を使用します:gdf.to_file("out.gpkg") |
参考資料
- GeoPandas ドキュメント:https://geopandas.org/
- GeoPandas GitHub:https://github.com/geopandas/geopandas
- Shapely ドキュメント:https://shapely.readthedocs.io/
- EPSG レジストリ:https://epsg.io/
関連スキル
- matplotlib-scientific-plotting — 高度な地図スタイル設定と図のエクスポート
- polars-dataframes — 空間演算の前後に行う高性能な表形式データ分析
- folium — geopandas.explore() を超える高度なインタラクティブ Web マッピング
コメント
Markdown 対応