Contents
環境構築とインストール手順
本章では、Python と空間解析用ライブラリを安全かつ再現性の高い形で導入する方法を解説します。
仮想環境を利用すれば依存関係の衝突を防げるだけでなく、プロジェクトごとに「実行環境=コード」のスナップショットを残すことができるため、チーム内共有や長期保守が格段に楽になります。以下では Windows・macOS・Linux のいずれでも同様に動作する手順と、実際のバージョン情報の取得例を示します。
Python と conda/venv のインストール
Python 本体およびパッケージ管理ツールは公式サイトまたは Miniconda から入手すると安全です。ここでは conda(Mamba を併用)と標準ライブラリの venv の両方を紹介します。
- Python ダウンロード
-
https://www.python.org/downloads/ から Python 3.12.x(2024‑11 時点で最新安定版)を取得し、インストーラ実行時に「Add Python to PATH」に必ずチェックします。
-
Miniconda の導入(推奨)
- https://docs.conda.io/en/latest/miniconda.html から OS に合った Miniconda3 (Python 3.12) をダウンロードしてインストールします。
- インストール後、ターミナルで以下を実行しバージョンが表示されれば成功です。
bash
conda --version # → conda 23.x.x
-
仮想環境の作成(conda と venv の両パターン)
-
conda を利用する場合
bash
# Python 3.12 環境を「sda」という名前で作成
conda create -n sda python=3.12 -y# 作成した環境へ切り替え
conda activate sda
-
venv を利用する場合(システムに Python が直接入っているケース)
bash
python -m venv sda # 仮想環境作成
source sda/bin/activate # macOS/Linux
.\sda\Scripts\activate # Windows
いずれの方法でも、python --version が 3.12.x を示せば OKです。
推奨環境設定
仮想環境が有効化されたら、空間解析に必要な主要ライブラリと Jupyter 系ツールを一括でインストールします。2024‑2025 年の最新安定版を対象としており、依存関係解決が高速な Mamba(conda の代替)を併用することを推奨します。
|
1 2 3 4 5 6 7 8 |
# Mamba のインストール(任意・省略可) conda install -n base -c conda-forge mamba -y # → mamba 1.5.x # 必要パッケージの一括インストール mamba install -c conda-forge \ geopandas==0.14.* shapely==2.0.* pyproj==3.6.* rasterio==1.3.* \ folium==0.15.* jupyterlab==4.2.* numpy==1.26.* pandas==2.2.* -y |
インストールが完了したら、バージョン情報を確認しておきましょう。
|
1 2 3 4 5 6 7 8 |
python -c "import geopandas, shapely, pyproj, rasterio, folium, jupyterlab; \ print('geopandas', geopandas.__version__); \ print('shapely', shapely.__version__); \ print('pyproj', pyproj.__version__); \ print('rasterio', rasterio.__version__); \ print('folium', folium.__version__); \ print('jupyterlab', jupyterlab.__version__)" |
実行環境例(2024‑11 時点)
- OS: Ubuntu 22.04 LTS / macOS Ventura / Windows 11
- Python: 3.12.5
- conda: 23.9.0、mamba: 1.5.8
- geopandas 0.14.4、shapely 2.0.3、pyproj 3.6.1、rasterio 1.3.10、folium 0.15.0、jupyterlab 4.2.5
JupyterLab を起動し、作業ノートブックとして Python 3 (sda) カーネルを選択すれば、以降のコードはそのまま実行できます。
空間データの基礎概念と主要フォーマット
このセクションでは、空間情報の二大表現であるベクトルデータとラスターデータの特徴を整理し、代表的なファイル形式について概要をつかみます。データ構造を正しく理解すれば、後述するライブラリ選択や前処理手順が自然に見えてきます。
ベクトルデータ vs ラスターデータ
ベクトルは点・線・面という離散的ジオメトリと属性テーブルの組み合わせで、位置情報を高精度かつ軽量に保持できます。一方ラスタは格子状(ピクセル)に数値を割り当てた2次元配列で、連続的な現象(例:衛星画像や標高)を表すのに適しています。
| 項目 | ベクトルデータ | ラスターデータ |
|---|---|---|
| 主な用途 | 境界線・道路ネットワーク | 衛星・航空画像、DEM(標高モデル) |
| データ構造 | Geometry + 属性テーブル | 2 次元配列(ピクセル) |
| 空間解析例 | バッファ、空間結合、オーバーレイ | リサンプリング、クリッピング、NDVI算出 |
| ファイルサイズ | 小〜中規模 | 大容量になることが多い |
ベクトルとラスタは相補的に使われるケースが多く、CRS(座標参照系)の統一 が解析の第一歩となります。
代表的ファイル形式
以下では、実務で頻出するフォーマットを3つ取り上げ、それぞれの長所と注意点をまとめます。コード例では geopandas.read_file() と rasterio.open() が自動判別して読み込めることを示します。
- Shapefile (.shp/.dbf)
-
ESRI が定義した古典的ベクトルフォーマット。1つのレイヤは .shp(ジオメトリ)・.dbf(属性)・.prj(CRS)等複数ファイルで構成されます。文字コードはデフォルトで
ISO‑8859-1、フィールド名は最大 10 文字という制約があります。 -
GeoJSON
-
JSON ベースの軽量ベクトル形式。UTF‑8 完全対応で Web API と相性が良く、属性情報も階層構造で保持できます。
geopandas.read_file()が直接読み込めるため、Jupyter Notebook 上で手早く可視化可能です。 -
GeoTIFF
- ラスタ画像に座標系情報(タグ)を埋め込んだ TIFF 拡張子。衛星画像や DEM の配布形式としてデファクトスタンダードです。
rasterio.open()が内部で GDAL を呼び出し、メタデータとバンド情報を取得します。
サンプルデータ取得先
- 国土地理院「行政区域」: https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03.html(Shapefile・GeoJSON)
- NASA Earthdata 「Landsat 8」: https://earthexplorer.usgs.gov/(GeoTIFF)
主要ライブラリのインポートとデータ読み込み・可視化
ここでは、実務でよく使う Python ライブラリをインポートし、バージョン確認からデータ読込、簡易的な可視化までの流れを示します。コードは Jupyter Notebook のセル単位でそのままコピー&ペーストできます。
ライブラリのインポートとバージョン確認
以下のスニペットは、利用する主要モジュールを一括でインポートし、実行環境が期待通りかどうかを確かめるための例です。numpy と pandas も同時にインストールしておくと、集計処理が快適になります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 基本ライブラリ import geopandas as gpd # ベクトル操作 import shapely.geometry as sg # ジオメトリ生成・変形 import pyproj # CRS 変換ユーティリティ import rasterio # ラスタ入出力 import folium # インタラクティブマップ # データ集計用 import pandas as pd import numpy as np # バージョン情報の表示(実行例) print("geopandas:", gpd.__version__) print("shapely :", sg.__module__) # shapely のモジュール名でバージョンは別途取得可 print("pyproj :", pyproj.__version__) print("rasterio:", rasterio.__version__) print("folium :", folium.__version__) |
備考
-shapelyのバージョンはimport shapely; print(shapely.__version__)で取得できます。
- JupyterLab 上では!conda list geopandasなどのシェルコマンドでも確認可能です。
ベクトルデータとラスターデータの読み込み例
次に、国土数値情報(N03)から取得した市区町村ポリゴンと、OpenStreetMap の道路レイヤーを同時にロードし、座標系を統一して Folium に描画する実装です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 1. ベクトルデータ読み込み municipalities = gpd.read_file("data/N03_2024_01_GML/municipality.geojson") roads = gpd.read_file( "https://download.geofabrik.de/japan-latest-free.shp.zip" ) # 2. CRS を WGS84 (EPSG:4326) に統一 target_crs = "EPSG:4326" municipalities = municipalities.to_crs(target_crs) roads = roads.to_crs(target_crs) # 3. Folium マップ作成(東京付近を中心に設定) m = folium.Map(location=[35.68, 139.76], zoom_start=10) # 4. 市区町村境界の描画 folium.GeoJson( municipalities.geometry, name="市区町村", style_function=lambda _: {"color": "black", "weight": 0.8} ).add_to(m) # 5. 道路レイヤーを重ねる(青色線で表示) folium.GeoJson( roads.geometry, name="道路ネットワーク", style_function=lambda _: {"color": "blue", "weight": 1} ).add_to(m) # 6. レイヤーコントロールの追加と保存 folium.LayerControl().add_to(m) m.save("output/municipality_roads.html") |
生成された output/municipality_roads.html はブラウザでインタラクティブにズーム・パンが可能です。ノートブック上では display(m) とすれば埋め込み表示できます。
ベクトル解析とラスターデータ処理の基本操作
この章では、実務で頻出する空間演算(バッファ、空間結合、オーバーレイ)と、ラスタ側のリプロジェクション・クリッピング・NDVI 計算を具体的なコード例とともに解説します。手順は「ジオメトリ同士の関係を数値化 → 属性集計 → 画像指標計算」の流れで整理しています。
ベクトル側の代表的操作
以下は、学校ポイントデータに対して 1 km バッファを作成し、その範囲内にある公園ポリゴンと空間結合する例です。target_crs はメートル単位(UTM 系)で統一している点に注意してください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 前提:既に target_crs が EPSG:32654 (UTM zone 54N) と決まっていると仮定 target_crs = "EPSG:32654" # 1. 学校ポイント(GeoJSON)を読み込み、CRS を統一 schools = gpd.read_file("data/schools.geojson").to_crs(target_crs) # 2. 1 km のバッファ作成(単位はメートル) schools["buffer"] = schools.geometry.buffer(1000) # 3. 公園ポリゴンを読み込み、同様に CRS 統一 parks = gpd.read_file("data/parks.geojson").to_crs(target_crs) # 4. バッファと公園の空間結合(intersects) joined = gpd.sjoin(parks, schools.set_geometry("buffer"), predicate="intersects") # 5. 学校ごとの公園面積を集計 area_by_school = ( joined.groupby("school_id") .agg({"geometry": "area"}) .rename(columns={"geometry": "park_area_m2"}) ) print(area_by_school.head()) |
buffer()は内部で Shapely の GEOS ライブラリを呼び出し、円形バッファを高速に生成します。sjoin()は R‑tree インデックスを自動構築するため、大規模ベクトルでも数秒程度で完了します。- 面積は CRS がメートル系の場合、
areaの単位が 平方メートル になることに留意してください。
オーバーレイ例(交差領域の抽出)
|
1 2 3 4 |
# parks と schools の「intersection」だけ取得 intersection = gpd.overlay(parks, schools, how="intersection") print(f"交差領域の件数: {len(intersection)}") |
ラスタ側の代表的操作
次に、Landsat 8 バンド 4(赤)とバンド 5(近赤外)から NDVI を算出し、学校バッファでクリップする手順です。rasterio.warp.reproject() と rasterio.mask.mask() が鍵になります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import rasterio, rasterio.mask, rasterio.warp, numpy as np # 1. 赤・近赤外バンドを同時に開く(メモリ確保は float32 に変換) with rasterio.open("data/LT08_20240301_B04.tif") as red_src, \ rasterio.open("data/LT08_20240301_B05.tif") as nir_src: red = red_src.read(1).astype('float32') nir = nir_src.read(1).astype('float32') meta = red_src.meta.copy() # メタ情報は赤バンドから取得 # 2. 必要なら CRS を UTM にリプロジェクション target_crs_raster = "EPSG:32654" if red_src.crs.to_string() != target_crs_raster: transform, width, height = rasterio.warp.calculate_default_transform( red_src.crs, target_crs_raster, red_src.width, red_src.height, *red_src.bounds) kwargs = meta.copy() kwargs.update({ "crs": target_crs_raster, "transform": transform, "width": width, "height": height }) # 再投影用配列を確保 reprojected_red = np.empty((height, width), dtype='float32') rasterio.warp.reproject( source=red, destination=reprojected_red, src_transform=red_src.transform, src_crs=red_src.crs, dst_transform=transform, dst_crs=target_crs_raster, resampling=rasterio.enums.Resampling.nearest) # 同様に NIR も再投影 reprojected_nir = np.empty((height, width), dtype='float32') rasterio.warp.reproject( source=nir, destination=reprojected_nir, src_transform=nir_src.transform, src_crs=nir_src.crs, dst_transform=transform, dst_crs=target_crs_raster, resampling=rasterio.enums.Resampling.nearest) red, nir = reprojected_red, reprojected_nir meta.update(kwargs) # 3. 学校バッファでクリップ(ジオメトリは GeoSeries のみ受け付ける) buffer_geom = schools.loc[0, "buffer"] # 任意の学校レコード clipped, out_transform = rasterio.mask.mask( red_src, [buffer_geom], crop=True, all_touched=False) meta.update({"height": clipped.shape[1], "width": clipped.shape[2], "transform": out_transform}) # 4. NDVI 計算(除算の 0 除算回避に epsilon を足す) ndvi = (nir - red) / (nir + red + 1e-6) # 5. GeoTIFF として保存 with rasterio.open("output/ndvi.tif", "w", **meta) as dst: dst.write(ndvi, 1) print("NDVI 計算・クリップ完了 → output/ndvi.tif") |
ポイントまとめ
- リプロジェクション は CRS が揃っていないと mask が期待通りに機能しません。rasterio.warp.reproject() が推奨です。
- クリッピング ではベクトルジオメトリをリストで渡すだけで簡潔に実装できます(all_touched=False はピクセルの中心が領域内かどうかで判定)。
- NDVI の正規化は 0‑1 に収めることが多く、np.nan が出ないように epsilon を足す慣習があります。
エンドツーエンドミニプロジェクトと次のステップ
ここでは、無料公開 API(OpenStreetMap Nominatim / Overpass) と USGS EarthExplorer の組み合わせで 「学校周辺 1 km 内にある公園を自動抽出し、Folium マップで可視化」 する一連のフローを示します。実装はすべて Jupyter Notebook 上で完結でき、再利用可能な関数としてまとめると社内共有が容易です。
無料ジオデータ API の取得例
| API | 主な用途 | 取得手段・注意点 |
|---|---|---|
| Nominatim (OpenStreetMap) | 住所や施設名から緯度経度を取得 | GET https://nominatim.openstreetmap.org/search?format=json&q=東京大学 → requests で呼び出し。商用利用はレートリミットと使用規約に注意 |
| Overpass API | OSM の属性タグ(例: leisure=park)を検索 | POST/GET にクエリ文字列を渡す。大規模取得は maxsize パラメータで制限回避が必要 |
| USGS EarthExplorer | Landsat、Sentinel、DEM 等の衛星画像取得 | API キー取得後、earthpy や requests で検索・ダウンロード。データサイズが大きいためストレージ確保が必須 |
ミニプロジェクト:学校周辺公園抽出・マップ化
以下は 大阪市立大学 を例に、Nominatim → Overpass → バッファ作成 → 空間結合 → Folium 可視化 までを一括で行うスクリプトです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import requests, json, geopandas as gpd, shapely.geometry as sg, folium # ------------------------------------------------- # 1. 学校座標取得(Nominatim) # ------------------------------------------------- def get_school_location(query: str) -> gpd.GeoDataFrame: """Nominatim で検索し、最初の結果を GeoDataFrame に変換""" resp = requests.get( "https://nominatim.openstreetmap.org/search", params={"q": query, "format": "json"}, headers={"User-Agent": "spatial-tutorial/1.0"} ) data = resp.json()[0] # 最初のヒットだけ使用 point = sg.Point(float(data["lon"]), float(data["lat"])) return gpd.GeoDataFrame( {"name": [data["display_name"]]}, geometry=[point], crs="EPSG:4326" ) school_gdf = get_school_location("大阪市立大学") print(school_gdf) # ------------------------------------------------- # 2. 1 km バッファ作成(メートル系に変換) # ------------------------------------------------- # UTM zone 54N (日本本土の多く) に投影 school_utm = school_gdf.to_crs("EPSG:32654") school_utm["buffer"] = school_utm.geometry.buffer(1000) # 1km # ------------------------------------------------- # 3. 公園データ取得(Overpass API) # ------------------------------------------------- overpass_url = "http://overpass-api.de/api/interpreter" query = f""" [out:json][timeout:25]; ( node["leisure"="park"](around:1000,{school_gdf.geometry.y[0]},{school_gdf.geometry.x[0]}); way["leisure"="park"](around:1000,{school_gdf.geometry.y[0]},{school_gdf.geometry.x[0]}); relation["leisure"="park"](around:1000,{school_gdf.geometry.y[0]},{school_gdf.geometry.x[0]}); ); out geom; """ resp = requests.get(overpass_url, params={"data": query}) parks_geojson = resp.json() # Overpass の出力は要素ごとに geometry が格納されているので GeoDataFrame に変換 def overpass_to_gdf(geojson) -> gpd.GeoDataFrame: features = [] for el in geojson["elements"]: if "geometry" not in el: continue # Point, LineString, Polygon のいずれかに対応 coords = [(pt["lon"], pt["lat"]) for pt in el["geometry"]] if el["type"] == "node": geom = sg.Point(coords[0]) elif el["type"] == "way": # 直線または閉じたポリゴンか判定 if coords[0] == coords[-1]: geom = sg.Polygon(coords) else: geom = sg.LineString(coords) else: # relation は複数要素になることが多いので簡易化 continue features.append({"type": el["tags"].get("leisure", "park"), "geometry": geom}) return gpd.GeoDataFrame(features, crs="EPSG:4326") parks_gdf = overpass_to_gdf(parks_geojson) # ------------------------------------------------- # 4. バッファ内の公園だけ抽出(空間結合) # ------------------------------------------------- # CRS を統一してから sjoin parks_utm = parks_gdf.to_crs("EPSG:32654") joined = gpd.sjoin(parks_utm, school_utm.set_geometry("buffer"), predicate="within") print(f"バッファ内の公園数: {len(joined)}") # ------------------------------------------------- # 5. Folium でマップ化 # ------------------------------------------------- m = folium.Map(location=[school_gdf.geometry.y[0], school_gdf.geometry.x[0]], zoom_start=15, tiles="Stamen Terrain") # 学校位置 folium.Marker( [school_gdf.geometry.y[0], school_gdf.geometry.x[0]], popup=school_gdf["name"][0], icon=folium.Icon(color='red', icon='university') ).add_to(m) # バッファ(円)表示 folium.Circle( location=[school_gdf.geometry.y[0], school_gdf.geometry.x[0]], radius=1000, color='orange', fill=False ).add_to(m) # 公園レイヤー(緑色) if not joined.empty: folium.GeoJson( joined.geometry, style_function=lambda _: {"color": "green"}, tooltip=folium.GeoJsonTooltip(fields=[], aliases=["Park"]) ).add_to(m) m.save("output/school_parks.html") print("マップ作成完了 → output/school_parks.html") |
実行上のポイント
| 項目 | 推奨策 |
|---|---|
| 大規模ベクトル(数十万件) | geopandas の PyGEOS バックエンドを有効化 (gpd.options.use_pygeos = True) で高速化 |
| ラスタのメモリ使用量 | 必要領域だけ rasterio.mask.mask() で切り出し、dtype を最小ビット幅に変換 |
| CRS の不一致エラー回避 | データ読み込み直後に必ず to_crs(target) で統一し、print(gdf.crs) で確認 |
| API レートリミット対策 | time.sleep(1) を挟むか、Overpass の maxsize パラメータでバッチ取得 |
次のステップ:自動化と CI/CD
- 関数化:上記スクリプトを
get_school_buffer(),fetch_parks(),create_map()などに分割し、再利用性を高めます。 - パラメータ管理:
yamlまたは.envファイルで API キー・検索語句・バッファ半径を外部化すると、ノートブック以外のスクリプトでも同一設定が使えます。 - GitHub Actions など CI 環境に組み込めば、データ取得 → 前処理 → 可視化までを定期実行し、最新マップを自動配信できます(例:GitHub Pages にデプロイ)。
まとめ
| 項目 | 要点 |
|---|---|
| 環境構築 | Python 3.12+Conda/Mamba 仮想環境で geopandas・shapely・rasterio・folium を一括インストール。バージョン確認コマンドを併記し、再現性を担保。 |
| データ概念 | ベクトルはジオメトリ+属性、ラスタは格子状数値。代表フォーマットは Shapefile/GeoJSON/GeoTIFF であり、CRS 統一が必須。 |
| 主要ライブラリ | geopandas と rasterio がデータ入出力の中心。JupyterLab + Folium によりインタラクティブな可視化が数行コードで実現可能。 |
| ベクトル操作 | バッファ、空間結合、オーバーレイは geopandas と内部の PyGEOS が高速に処理。属性集計は Pandas の groupby で完結。 |
| ラスタ処理 | CRS リプロジェクションは rasterio.warp.reproject()、クリッピングは `mask()”、NDVI はシンプルな配列演算で実装。 |
| ミニプロジェクト | Nominatim + Overpass API で「学校周辺 1 km の公園」抽出 → Folium マップ化。関数化・CI により自動化が容易。 |
| パフォーマンス対策 | PyGEOS バックエンド、メモリ削減のための領域切り出し、CRS 統一チェック、API のレートリミット回避を徹底すれば、大規模データでも安定稼働。 |
本稿で紹介した手順とコードは、「ローカル PC だけで完結する空間解析パイプライン」 を構築する最小構成です。これらをベースに、機械学習モデルの組み込みや Web ダッシュボードへの展開など、さらに高度なプロジェクトへと拡張していくことが可能です。ぜひ実際に手を動かし、データドリブンな地理情報分析を体感してください。