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?

ラスターデータの外枠

0
Last updated at Posted at 2024-04-24

こんな図を作ります

image.png
ググっても出てこなかったので作りました.↓

ラスターデータの外枠をcartopy上で描写

全球-180, 180, -90, 90の範囲で
解像度5分(赤道で9km)の0と1で構成されたラスターデータを考えます
x軸方向に4320
y軸方向に2160
グリッド存在する空間だと思ってください
1の部分は東京の都市域を表す都市マスクです
全球のうち138-141E, 34-37Nの範囲をcartopyで描写します
描写された空間内に1で示された都市マスクの外枠をハイライト

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

one = 138
two = 141
three = 34
four = 37
upperindex = (90-four)*12
lowerindex = (90-three)*12
leftindex = (180+one)*12
rightindex = (180+two)*12
img_extent = (one, two, three, four)
delta = 360/4320
x_left = img_extent[0]
y_top = img_extent[3]
    
raster_data = mask[upperindex:lowerindex, leftindex:rightindex]

# outer
upper_indices = []
lower_indices = []
left_indices = []
right_indices = []
rows, cols = raster_data.shape
for i in range(rows):
    for j in range(cols):
        if raster_data[i, j] == 1:
            if raster_data[i-1, j] == 0:
                upper_indices.append((i, j))
            if raster_data[i+1, j] == 0:
                lower_indices.append((i, j))
            if raster_data[i, j-1] == 0:
                left_indices.append((i, j))
            if raster_data[i, j+1] == 0:
                right_indices.append((i, j))
                
fig = plt.figure(figsize=(10,10))
ax = plt.subplot(projection=ccrs.PlateCaree())
ax.coastlines()
ax.set_extent(img_extent)
ax.gridlines(draw_labels=True)               

for up in upper_indices:
    x_index = x_left + delta*(up[1])
    y_index = y_top - delta*(up[0])
    ax.plot([x_index, x_index+delta], [y_index, y_index], color='red', transform=ccrs.PlateCarree())
for lo in lower_indices:
    x_index = x_left + delta*(lo[1])
    y_index = y_top - delta*(lo[0]+1)
    ax.plot([x_index, x_index+delta], [y_index, y_index], color='red', transform=ccrs.PlateCarree())
for le in left_indices:
    x_index = x_left + delta*(le[1])
    y_index = y_top - delta*(le[0]+1)
    ax.plot([x_index, x_index], [y_index, y_index+delta], color='red', transform=ccrs.PlateCarree())
for ri in right_indices:
    x_index = x_left + delta*(ri[1]+1)
    y_index = y_top - delta*(ri[0]+1)
    ax.plot([x_index, x_index], [y_index, y_index+delta], color='red', transform=ccrs.PlateCarree())

plt.show()

追記 (20260323)

LLM全盛期になったので上記のscriptをreviewしてもらった。
結果 > np.stackを使えば全球でも一瞬で境界線を取得できた(すごい)

  1. 横線をすべて取得
  2. 縦線をすべて取得
  3. 最後にそれらをconcatinateする
from __future__ import annotations

from pathlib import Path
import time

import numpy as np

# ============================================================
# Settings
# ============================================================
H08DIR = "/your/directory/name"
SUF    = ".bin"
NX = 4320
NY = 2160
DX = 360.0 / NX   # 5/60 degrees
DY = 180.0 / NY
L  = NX * NY

OUT_DIR  = Path(H08DIR) / "URB/out"
OUT_PATH = OUT_DIR / "boundary_segments.npy"
cty_msk_path = h08 / "map/dat/cty_msk_" / f"GPW_____00000000{SUF}"

P0MIS = np.float32(1.0e20)


# ============================================================
# Helper functions
# ============================================================
def to_2d_grid(data_1d, l2x, l2y):
    grid = np.full((NY, NX), np.nan)
    grid[l2y, l2x] = data_1d
    return grid


# ============================================================
# Main
# ============================================================
def main():
    h08 = Path(H08DIR)

    # --- Load city mask ---
    print("Loading city mask ...")
    cty_raw = np.fromfile(str(cty_msk_path), dtype="<f4", count=L)
    cty_id = np.where(
        (cty_raw >= np.float32(P0MIS) * 0.9) | ~np.isfinite(cty_raw),
        0,
        cty_raw.astype(np.int32),
    )

    # --- Map to 2D grid ---
    print("Mapping to 2D grid ...")
    cty_float = np.where(cty_id > 0, cty_id.astype(np.float64), 0.0)
    cty_2d = cty_float.reshape(NY, NX)
    cty_2d = np.nan_to_num(cty_2d, nan=0.0)

    delta = DX  # = DY = 5/60 degrees

    # Cell edge coordinates
    lon_edge = -180.0 + np.arange(NX + 1) * delta
    lat_edge = 90.0  - np.arange(NY + 1) * delta

    # Vertical edges (between column x and x+1)
    diff_v = (cty_2d[:, :-1] != cty_2d[:, 1:])
    has_city_v = (cty_2d[:, :-1] > 0) | (cty_2d[:, 1:] > 0)
    ys_v, xs_v = np.where(diff_v & has_city_v)

    x_pos = lon_edge[xs_v + 1]
    y_top = lat_edge[ys_v]
    y_bot = lat_edge[ys_v + 1]
    v_segs = np.stack([
        np.column_stack([x_pos, y_top]),
        np.column_stack([x_pos, y_bot]),
    ], axis=1)

    # Horizontal edges (between row y and y+1)
    diff_h = (cty_2d[:-1, :] != cty_2d[1:, :])
    has_city_h = (cty_2d[:-1, :] > 0) | (cty_2d[1:, :] > 0)
    ys_h, xs_h = np.where(diff_h & has_city_h)

    x_left  = lon_edge[xs_h]
    x_right = lon_edge[xs_h + 1]
    y_pos   = lat_edge[ys_h + 1]
    h_segs = np.stack([
        np.column_stack([x_left,  y_pos]),
        np.column_stack([x_right, y_pos]),
    ], axis=1)

    segments = np.concatenate([v_segs, h_segs], axis=0)

    # --- Save ---
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    np.save(str(OUT_PATH), segments)
    print(f"\nSaved: {OUT_PATH}")


if __name__ == "__main__":
    main()

下記画像はDelhiとその周辺の都市をzoom-upして表示した例です。
ちゃんと都市境界が赤線で表示されています。
image.png
描写はLineCollectionで線の集合として一気に描写できてしまいます。
cartopy上では下記のようにplotします。

def _overlay_boundary(ax, segments, transform,
                      linewidth=0.8, color="black"):

    if segments is None or len(segments) == 0:
        return

    # Clip to axes extent for performance
    extent = ax.get_extent(crs=transform)  # (x0, x1, y0, y1)
    x0, x1, y0, y1 = extent
    seg_x = segments[:, :, 0]  # (N, 2)
    seg_y = segments[:, :, 1]  # (N, 2)
    in_extent = (
        (seg_x.max(axis=1) >= x0) & (seg_x.min(axis=1) <= x1) &
        (seg_y.max(axis=1) >= y0) & (seg_y.min(axis=1) <= y1)
    )
    visible = segments[in_extent]

    if len(visible) == 0:
        return

    lc = LineCollection(visible, colors=color, linewidths=linewidth,
                        transform=transform, zorder=3)
    ax.add_collection(lc)
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?