0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Sentinel-2のデータをAWS 公開データから取得し、NDVI を可視化してみた

Posted at

はじめに

これは Fusic Advent Calendar 2025 の20日目の記事です。前日はDaiki Urataによる「Cursor + Figma Slidesで実現するAIアシストなPresentation as Code」でした。

本記事では、Sentinel-2のデータをAWS 公開データから取得し、NDVI を可視化してみた話をします!

きっかけ

「人工衛星データ解析」と聞くと、

  • GIS の専門知識が必要そう
  • 高価なソフトや環境が必要そう
  • 仕事でしか触れなさそう

という印象があるかもしれません。

しかし実は、Sentinel-2 の衛星データは AWS 上で無料公開されており、
Python + ローカル環境だけでも取得して分析することが可能です。

この記事では、

クラウド(AWS)に公開されている人工衛星データをPythonで読み取り
植生指数(NDVI)を計算して
カラフルな画像として可視化する

ところまでを簡単にやってみます。

使用技術

  • Python 3.12

  • AWS Open Data(Sentinel-2)

  • STAC API(Earth Search)

  • rasterio / numpy / Pillow

構成はとてもシンプルです。

Sentinel-2(AWS公開データ)
        ↓
  Python(ローカル)
        ↓
     NDVI画像(PNG)

Sentinel-2とは?

ESA(欧州宇宙機関)の地球観測衛星
約5日に1回、地球全体を観測
複数の「バンド(波長)」を持つ

今回は

  • B04(Red)
  • B08(NIR: 近赤外)

のバンドを使います。

データ取得方法:STAC API

AWS には Sentinel-2 の COG(Cloud Optimized GeoTIFF) が公開されており、
HTTP 経由で必要な部分だけ読み込めるのが特徴です。

今回は Earth Search の STAC API を使って、

  • 地点(bbox)
  • 日付
  • 雲量

を条件にシーンを検索します。

今回の解析では、以下の範囲を対象に Sentinel-2 の衛星データを取得しています。

bbox = [130.30, 33.50, 130.60, 33.70]

これは 福岡県・福岡市周辺 を表すバウンディングボックス(矩形範囲)です。
bbox は以下の順番で指定します。
[minLongitude, minLatitude, maxLongitude, maxLatitude]

プロジェクト構成

  requirements.txt
  stac.py
  ndvi.py
  local_run.py

使用するライブラリ

# requirements.txt
requests
rasterio
numpy
Pillow

STAC で Sentinel-2 を検索する(stac.py)

STAC(SpatioTemporal Asset Catalog)は、「いつ・どこで・どんな地理データがあるか」をJSON API で検索できる仕組みです。
以下のコードでSentinel-2 の公開データを STAC API で検索し、条件に合う 1シーンを取得。NDVI 計算に必要な Red / NIR バンドのURLを取り出しています。

import requests

STAC_URL = "https://earth-search.aws.element84.com/v1/search"

def search_sentinel2(bbox, date_start, date_end, max_cloud=20):
    payload = {
        "collections": ["sentinel-2-l2a"],
        "bbox": bbox,
        "datetime": f"{date_start}/{date_end}",
        "query": {
            "eo:cloud_cover": {"lte": max_cloud}
        },
        "limit": 1
    }

    res = requests.post(STAC_URL, json=payload)
    res.raise_for_status()
    item = res.json()["features"][0]
    return item

def pick_bands(item):
    red = item["assets"]["B04"]["href"]
    nir = item["assets"]["B08"]["href"]
    return red, nir

NDVI を計算してカラー化(ndvi.py)

以下のコードでは、衛星画像(COG)を HTTP越しに直接読み込み、NDVI を数値として計算、疑似カラー画像に変換しています。

import numpy as np
import rasterio
from PIL import Image
import io

def read_band(href):
    with rasterio.open(href) as ds:
        return ds.read(1).astype(np.float32)

def compute_ndvi(red_href, nir_href):
    red = read_band(red_href)
    nir = read_band(nir_href)
    ndvi = (nir - red) / (nir + red)
    return np.clip(ndvi, -1, 1)

def ndvi_to_color_png(ndvi):
    ndvi = np.nan_to_num(ndvi, nan=-1.0)
    t = (ndvi + 1) / 2

    rgb = np.zeros((*t.shape, 3), dtype=np.uint8)
    rgb[..., 0] = (255 * (1 - t)).astype(np.uint8)   # 赤
    rgb[..., 1] = (180 * t).astype(np.uint8)         # 緑

    img = Image.fromarray(rgb, "RGB")
    buf = io.BytesIO()
    img.save(buf, format="PNG")
    return buf.getvalue()

実行ファイル(local_run.py)

from stac import search_sentinel2, pick_bands
from ndvi import compute_ndvi, ndvi_to_color_png

bbox = [130.30, 33.50, 130.60, 33.70]  # 福岡市周辺
date_start = "2025-07-01"
date_end = "2025-07-31"

item = search_sentinel2(bbox, date_start, date_end)
red, nir = pick_bands(item)

ndvi = compute_ndvi(red, nir)
png = ndvi_to_color_png(ndvi)

with open("ndvi.png", "wb") as f:
    f.write(png)

print("ndvi.png を出力しました")

出力される画像

今回2025年7月1日~7月31日の範囲で、福岡市の画像をみた結果が以下です。

スクリーンショット 2025-12-20 1.38.27.png

福岡市都心部にはあまり植生はないですが、東の方にいくとたくさん緑があるのがわかります。
また、大濠公園や福岡空港、那珂川があることもよくわかりますね。

おわりに

人工衛星のデータ解析というと難しそうですが、

「公開データをローカルでちょっと触る」

だけでも十分楽しい世界です。

ぜひチャレンジしてみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?