第15章 - 实战案例与项目应用

第15章:实战案例与项目应用

15.1 概述

本章通过实际项目案例,展示如何综合运用 GDAL 的各项功能解决实际问题。这些案例涵盖了遥感影像处理、地理数据转换、空间分析等多个领域。

15.2 案例一:批量影像预处理流水线

15.2.1 需求分析

对一批卫星影像进行标准化预处理:

  • 坐标系统统一到 EPSG:4326
  • 裁剪到指定区域
  • 重采样到统一分辨率
  • 输出为压缩的 GeoTIFF 格式

15.2.2 Python 实现

from osgeo import gdal, osr
import os
from pathlib import Path

class ImageProcessor:
    """影像批量处理器"""
    
    def __init__(self, output_srs='EPSG:4326', resolution=0.0001):
        self.output_srs = output_srs
        self.resolution = resolution
        gdal.SetCacheMax(1024 * 1024 * 1024)  # 1GB 缓存
        
    def process_image(self, input_path, output_path, bbox=None):
        """处理单个影像"""
        print(f"处理: {input_path}")
        
        src_ds = gdal.Open(input_path)
        if src_ds is None:
            raise RuntimeError(f"无法打开文件: {input_path}")
        
        try:
            warp_options = {
                'format': 'GTiff',
                'dstSRS': self.output_srs,
                'xRes': self.resolution,
                'yRes': self.resolution,
                'resampleAlg': gdal.GRA_Bilinear,
                'creationOptions': ['COMPRESS=LZW', 'TILED=YES']
            }
            
            if bbox:
                warp_options['outputBounds'] = bbox
            
            ds = gdal.Warp(output_path, src_ds, **warp_options)
            
            if ds:
                print(f"✓ 完成: {output_path}")
                ds = None
                return True
            else:
                return False
        finally:
            src_ds = None
    
    def batch_process(self, input_dir, output_dir, bbox=None):
        """批量处理目录中的所有影像"""
        input_path = Path(input_dir)
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
        
        extensions = ['.tif', '.tiff', '.img', '.jp2']
        success_count = 0
        fail_count = 0
        
        for img_file in input_path.iterdir():
            if img_file.suffix.lower() in extensions:
                out_file = output_path / f"{img_file.stem}_processed.tif"
                try:
                    if self.process_image(str(img_file), str(out_file), bbox):
                        success_count += 1
                    else:
                        fail_count += 1
                except Exception as e:
                    print(f"✗ 错误: {img_file.name} - {e}")
                    fail_count += 1
        
        print(f"\n处理完成: 成功 {success_count}, 失败 {fail_count}")

# 使用示例
processor = ImageProcessor(output_srs='EPSG:4326', resolution=0.00025)
beijing_bbox = (115.7, 39.4, 117.4, 41.6)
processor.batch_process('./raw_images', './processed_images', bbox=beijing_bbox)

15.3 案例二:地理数据 ETL 系统

15.3.1 完整实现

from osgeo import ogr, osr
import logging

class GeoDataETL:
    """地理数据 ETL 处理器"""
    
    def __init__(self):
        self.logger = logging.getLogger('GeoDataETL')
        self.logger.setLevel(logging.INFO)
    
    def extract(self, source_path, layer_name=None):
        """提取数据"""
        self.logger.info(f"提取数据: {source_path}")
        
        ds = ogr.Open(source_path, 0)
        if ds is None:
            raise RuntimeError(f"无法打开数据源: {source_path}")
        
        layer = ds.GetLayerByName(layer_name) if layer_name else ds.GetLayer(0)
        if layer is None:
            raise RuntimeError(f"无法获取图层: {layer_name}")
        
        return layer, ds
    
    def transform(self, layer, target_srs='EPSG:4326', attribute_filter=None):
        """转换数据"""
        if attribute_filter:
            layer.SetAttributeFilter(attribute_filter)
        
        source_srs = layer.GetSpatialRef()
        target_srs_obj = osr.SpatialReference()
        target_srs_obj.SetFromUserInput(target_srs)
        
        transform = None
        if not source_srs.IsSame(target_srs_obj):
            transform = osr.CoordinateTransformation(source_srs, target_srs_obj)
        
        return transform, target_srs_obj
    
    def load(self, layer, output_path, format_name='ESRI Shapefile',
             transform=None, target_srs=None):
        """加载数据到目标"""
        driver = ogr.GetDriverByName(format_name)
        if os.path.exists(output_path):
            driver.DeleteDataSource(output_path)
        
        out_ds = driver.CreateDataSource(output_path)
        out_layer = out_ds.CreateLayer(
            'output',
            srs=target_srs if target_srs else layer.GetSpatialRef(),
            geom_type=layer.GetGeomType()
        )
        
        # 复制字段定义
        layer_defn = layer.GetLayerDefn()
        for i in range(layer_defn.GetFieldCount()):
            out_layer.CreateField(layer_defn.GetFieldDefn(i))
        
        # 复制要素
        count = 0
        for feature in layer:
            out_feature = ogr.Feature(out_layer.GetLayerDefn())
            for i in range(layer_defn.GetFieldCount()):
                out_feature.SetField(i, feature.GetField(i))
            
            geom = feature.GetGeometryRef()
            if geom:
                geom_copy = geom.Clone()
                if transform:
                    geom_copy.Transform(transform)
                out_feature.SetGeometry(geom_copy)
            
            out_layer.CreateFeature(out_feature)
            count += 1
        
        self.logger.info(f"✓ 成功加载 {count} 个要素")
        out_ds = None

# 使用示例
etl = GeoDataETL()
etl.run(source_path='input.shp', output_path='output.geojson', 
        format='GeoJSON', target_srs='EPSG:4326')

15.4 案例三:DEM 地形分析

from osgeo import gdal

class DEMAnalyzer:
    """DEM 地形分析器"""
    
    def __init__(self, dem_path):
        self.ds = gdal.Open(dem_path)
        if self.ds is None:
            raise RuntimeError(f"无法打开 DEM: {dem_path}")
    
    def calculate_slope(self, output_path):
        """计算坡度"""
        gdal.DEMProcessing(output_path, self.ds, 'slope',
                          slopeFormat='percent', computeEdges=True)
        print(f"✓ 坡度已保存到: {output_path}")
    
    def calculate_aspect(self, output_path):
        """计算坡向"""
        gdal.DEMProcessing(output_path, self.ds, 'aspect', computeEdges=True)
        print(f"✓ 坡向已保存到: {output_path}")
    
    def calculate_hillshade(self, output_path, azimuth=315, altitude=45):
        """计算山体阴影"""
        gdal.DEMProcessing(output_path, self.ds, 'hillshade',
                          azimuth=azimuth, altitude=altitude, computeEdges=True)
        print(f"✓ 山体阴影已保存到: {output_path}")

# 使用示例
analyzer = DEMAnalyzer('dem.tif')
analyzer.calculate_slope('slope.tif')
analyzer.calculate_aspect('aspect.tif')
analyzer.calculate_hillshade('hillshade.tif')

15.5 性能监控

import time
import psutil
from contextlib import contextmanager

@contextmanager
def performance_monitor(operation_name):
    """性能监控上下文管理器"""
    process = psutil.Process()
    start_time = time.time()
    start_memory = process.memory_info().rss / 1024 / 1024
    
    print(f"\n{'='*60}")
    print(f"开始: {operation_name}")
    print(f"{'='*60}")
    
    try:
        yield
    finally:
        end_time = time.time()
        end_memory = process.memory_info().rss / 1024 / 1024
        
        print(f"\n{'='*60}")
        print(f"完成: {operation_name}")
        print(f"耗时: {end_time - start_time:.2f} 秒")
        print(f"内存使用: {end_memory - start_memory:.2f} MB")
        print(f"{'='*60}\n")

# 使用示例
with performance_monitor("影像重采样"):
    gdal.Warp('output.tif', 'input.tif', xRes=30, yRes=30)

15.6 Docker 容器化部署

# Dockerfile
FROM osgeo/gdal:ubuntu-full-latest

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

ENV GDAL_CACHEMAX=1024
ENV GDAL_NUM_THREADS=ALL_CPUS

CMD ["python", "main.py"]

15.7 本章小结

本章通过三个完整的实战案例,展示了 GDAL 在实际项目中的应用:

  1. 影像预处理流水线:自动化批量处理遥感影像
  2. 地理数据 ETL 系统:构建完整的数据转换流程
  3. DEM 地形分析:地形指标计算和分析

关键要点:

  • 合理使用缓存和并行处理提升性能
  • 完善的错误处理和日志记录
  • 生产环境的配置和部署
  • 性能监控和优化

相关资源

posted @ 2025-12-29 10:48  我才是银古  阅读(1)  评论(0)    收藏  举报