Compare commits
3 Commits
bdac7873f4
...
4469d6462d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4469d6462d | |||
| 237fcba647 | |||
| 7fbc613ded |
@ -597,18 +597,15 @@ class ContentMapper:
|
||||
grid_y = grid_yy[:, 0]
|
||||
total_cells = len(grid_x) * len(grid_y)
|
||||
|
||||
# 局部窗口:每个块使用 500m 空间窗口内的采样点
|
||||
_LOCAL_WINDOW = 500.0 # 米
|
||||
|
||||
_n_workers = min(os.cpu_count() or 4, 8)
|
||||
print(f"正在使用 局部克里金 (Local Kriging):"
|
||||
f"网格={total_cells:,} 点, 窗口={_LOCAL_WINDOW}m, "
|
||||
print(f"正在使用 局部克里金 (自适应分块 + 40% 重叠缓冲):"
|
||||
f"网格={total_cells:,} 点, "
|
||||
f"{_n_workers} workers")
|
||||
|
||||
grid_content = self._local_kriging(
|
||||
points, values, grid_x, grid_y,
|
||||
window_size=_LOCAL_WINDOW,
|
||||
n_workers=_n_workers,
|
||||
n_closest_points=50,
|
||||
)
|
||||
|
||||
valid_mask = ~np.isnan(grid_content)
|
||||
@ -695,84 +692,94 @@ class ContentMapper:
|
||||
return grid_content
|
||||
|
||||
def _local_kriging(self, points, values, grid_x, grid_y,
|
||||
window_size=500.0, n_workers=4):
|
||||
"""局部克里金:网格按 window_size 分块,每块只用块内+缓冲区的局部采样点
|
||||
n_workers=4, n_closest_points=50):
|
||||
"""局部克里金:自适应分块 + 重叠缓冲区 + 保护性近邻限制
|
||||
|
||||
核心思想:协方差矩阵锁定在局部 n×n(几十到几百),而非全局 8242×8242。
|
||||
千万级网格秒级完成,精度几乎无损。
|
||||
1. 自适应块大小: 根据 extent 自动切分为 ~4×4 块 (16~25块)
|
||||
2. 重叠缓冲区: 采样点范围扩展块长宽的 40%,交界处平滑无拼缝
|
||||
3. 保护性近邻: n_closest_points=50,稀释极端异常值
|
||||
4. 网格点仅使用严格不重叠的块范围(Buffer 仅用于筛选采样点)
|
||||
"""
|
||||
import multiprocessing
|
||||
|
||||
# 计算网格空间范围
|
||||
x_min, x_max = float(grid_x[0]), float(grid_x[-1])
|
||||
y_min, y_max = float(grid_y[0]), float(grid_y[-1])
|
||||
grid_dx = float(grid_x[1] - grid_x[0]) if len(grid_x) > 1 else 1.0
|
||||
grid_dy = float(grid_y[1] - grid_y[0]) if len(grid_y) > 1 else 1.0
|
||||
extent_x = x_max - x_min
|
||||
extent_y = y_max - y_min
|
||||
|
||||
# 按 window_size 将空间分割为不重叠的块
|
||||
# 每个块向四周扩展一个 window_size 作为缓冲区(获取局部采样点)
|
||||
n_blocks_x = max(1, int(np.ceil((x_max - x_min) / window_size)))
|
||||
n_blocks_y = max(1, int(np.ceil((y_max - y_min) / window_size)))
|
||||
block_dx = (x_max - x_min) / n_blocks_x
|
||||
block_dy = (y_max - y_min) / n_blocks_y
|
||||
# ── 自适应块数:目标 4×4 = 16 块(最少 2×2,最多 6×6)──
|
||||
_TARGET_BLOCKS = 4
|
||||
n_blocks_x = max(2, min(6, _TARGET_BLOCKS))
|
||||
n_blocks_y = max(2, min(6, _TARGET_BLOCKS))
|
||||
# 若 extent 纵横比极端,在较长维度上增加块数
|
||||
if extent_y > extent_x * 1.5:
|
||||
n_blocks_y = min(6, n_blocks_y + 1)
|
||||
elif extent_x > extent_y * 1.5:
|
||||
n_blocks_x = min(6, n_blocks_x + 1)
|
||||
|
||||
block_dx = extent_x / n_blocks_x
|
||||
block_dy = extent_y / n_blocks_y
|
||||
# 缓冲区 = 块长宽的 40%(兼顾平滑与计算量)
|
||||
buffer_x = block_dx * 0.4
|
||||
buffer_y = block_dy * 0.4
|
||||
|
||||
total_blocks = n_blocks_x * n_blocks_y
|
||||
print(f" 空间分块: {n_blocks_x}×{n_blocks_y} = {total_blocks} 块 "
|
||||
f"(窗口={window_size}m, 缓冲区={window_size}m)")
|
||||
print(f" 自适应分块: {n_blocks_x}×{n_blocks_y} = {total_blocks} 块 "
|
||||
f"(块≈{block_dx:.0f}×{block_dy:.0f}m, 缓冲={buffer_x:.0f}×{buffer_y:.0f}m, "
|
||||
f"n_closest={n_closest_points})")
|
||||
|
||||
# 构建任务列表
|
||||
tasks = []
|
||||
for iy in range(n_blocks_y):
|
||||
for ix in range(n_blocks_x):
|
||||
# 块范围(不含缓冲区)
|
||||
# 严格不重叠的块范围
|
||||
bx_min = x_min + ix * block_dx
|
||||
bx_max = x_min + (ix + 1) * block_dx
|
||||
by_min = y_min + iy * block_dy
|
||||
by_max = y_min + (iy + 1) * block_dy
|
||||
|
||||
# 带缓冲区的搜索范围
|
||||
search_xmin = bx_min - window_size
|
||||
search_xmax = bx_max + window_size
|
||||
search_ymin = by_min - window_size
|
||||
search_ymax = by_max + window_size
|
||||
|
||||
# 缓冲区内的网格点索引
|
||||
grid_mask_x = (grid_x >= search_xmin) & (grid_x <= search_xmax)
|
||||
grid_mask_y = (grid_y >= search_ymin) & (grid_y <= search_ymax)
|
||||
if not np.any(grid_mask_x) or not np.any(grid_mask_y):
|
||||
# 块自身的纯净网格(不含缓冲)
|
||||
gmask_x = (grid_x >= bx_min) & (grid_x <= bx_max)
|
||||
gmask_y = (grid_y >= by_min) & (grid_y <= by_max)
|
||||
if not np.any(gmask_x) or not np.any(gmask_y):
|
||||
continue
|
||||
|
||||
sub_grid_x = grid_x[grid_mask_x]
|
||||
sub_grid_y = grid_y[grid_mask_y]
|
||||
sub_grid_x = grid_x[gmask_x]
|
||||
sub_grid_y = grid_y[gmask_y]
|
||||
|
||||
# 缓冲区内的采样点
|
||||
point_mask = (
|
||||
(points[:, 0] >= search_xmin) & (points[:, 0] <= search_xmax) &
|
||||
(points[:, 1] >= search_ymin) & (points[:, 1] <= search_ymax)
|
||||
# 缓冲区采样点范围(40% 重叠)
|
||||
sx_min = bx_min - buffer_x
|
||||
sx_max = bx_max + buffer_x
|
||||
sy_min = by_min - buffer_y
|
||||
sy_max = by_max + buffer_y
|
||||
|
||||
pmask = (
|
||||
(points[:, 0] >= sx_min) & (points[:, 0] <= sx_max) &
|
||||
(points[:, 1] >= sy_min) & (points[:, 1] <= sy_max)
|
||||
)
|
||||
local_pts = points[point_mask]
|
||||
local_vals = values[point_mask]
|
||||
local_pts = points[pmask]
|
||||
local_vals = values[pmask]
|
||||
|
||||
sub_n = len(sub_grid_x) * len(sub_grid_y)
|
||||
tasks.append((
|
||||
local_pts, local_vals,
|
||||
sub_grid_x, sub_grid_y,
|
||||
bx_min, bx_max, by_min, by_max, # 只保留核心区域结果
|
||||
grid_dx, grid_dy,
|
||||
bx_min, bx_max, by_min, by_max,
|
||||
n_closest_points,
|
||||
ix, iy, total_blocks,
|
||||
))
|
||||
|
||||
print(f" 有效块: {len(tasks)} (含采样点的空间块)")
|
||||
print(f" 有效块: {len(tasks)}")
|
||||
|
||||
if len(tasks) <= 1:
|
||||
return self._local_krige_block(tasks[0])
|
||||
return self._local_krige_block(*tasks[0])
|
||||
|
||||
# 多进程执行
|
||||
print(f" 启动 {min(n_workers, len(tasks))} 个 worker 进程...")
|
||||
with multiprocessing.Pool(processes=min(n_workers, len(tasks))) as pool:
|
||||
results = pool.map(_local_krige_block_worker, tasks)
|
||||
|
||||
# 拼接结果:初始化全 NaN 数组,逐块填充核心区域
|
||||
# 拼接:全 NaN 数组,逐块填回
|
||||
grid_full = np.full((len(grid_y), len(grid_x)), np.nan, dtype=np.float64)
|
||||
for (block_result, bx_min, bx_max, by_min, by_max) in results:
|
||||
if block_result is None:
|
||||
@ -794,11 +801,10 @@ class ContentMapper:
|
||||
def _local_krige_block(self, local_pts, local_vals,
|
||||
sub_grid_x, sub_grid_y,
|
||||
bx_min, bx_max, by_min, by_max,
|
||||
grid_dx, grid_dy,
|
||||
n_closest=50,
|
||||
block_ix=0, block_iy=0, total=1):
|
||||
"""单块局部克里金"""
|
||||
n_pts = len(local_pts)
|
||||
n_cells = len(sub_grid_x) * len(sub_grid_y)
|
||||
if n_pts < 3:
|
||||
return None, bx_min, bx_max, by_min, by_max
|
||||
|
||||
@ -811,7 +817,7 @@ class ContentMapper:
|
||||
z, ss = ok.execute(
|
||||
'grid', sub_grid_x, sub_grid_y,
|
||||
backend='loop',
|
||||
n_closest_points=min(15, n_pts),
|
||||
n_closest_points=min(n_closest, n_pts),
|
||||
)
|
||||
return np.array(z), bx_min, bx_max, by_min, by_max
|
||||
|
||||
@ -3285,7 +3291,7 @@ def _local_krige_block_worker(args):
|
||||
"""单个局部克里金块任务(独立进程入口,必须为模块级函数)"""
|
||||
(local_pts, local_vals, sub_grid_x, sub_grid_y,
|
||||
bx_min, bx_max, by_min, by_max,
|
||||
grid_dx, grid_dy, block_ix, block_iy, total) = args
|
||||
n_closest, block_ix, block_iy, total) = args
|
||||
|
||||
import numpy as np
|
||||
from pykrige.ok import OrdinaryKriging
|
||||
@ -3293,8 +3299,10 @@ def _local_krige_block_worker(args):
|
||||
if len(local_pts) < 3:
|
||||
return (None, bx_min, bx_max, by_min, by_max)
|
||||
|
||||
print(f" [LocalKrige] 块 ({block_iy},{block_ix}) [{block_iy * 100 + block_ix}/{total}] "
|
||||
f"采样点={len(local_pts)}, 网格={len(sub_grid_x)}×{len(sub_grid_y)}")
|
||||
print(f" [LocalKrige] 块 ({block_iy},{block_ix}) "
|
||||
f"采样点={len(local_pts)}, "
|
||||
f"网格={len(sub_grid_x)}×{len(sub_grid_y)}, "
|
||||
f"n_closest={min(n_closest, len(local_pts))}")
|
||||
|
||||
ok = OrdinaryKriging(
|
||||
local_pts[:, 0], local_pts[:, 1], local_vals,
|
||||
@ -3305,6 +3313,6 @@ def _local_krige_block_worker(args):
|
||||
z, ss = ok.execute(
|
||||
'grid', sub_grid_x, sub_grid_y,
|
||||
backend='loop',
|
||||
n_closest_points=min(15, len(local_pts)),
|
||||
n_closest_points=min(n_closest, len(local_pts)),
|
||||
)
|
||||
return (np.array(z), bx_min, bx_max, by_min, by_max)
|
||||
|
||||
Reference in New Issue
Block a user