1. 前言

在遥感技术和地球观测领域,处理多源异构原始影像数据是一个关键挑战。随着卫星和传感器技术的进步,我们获得了前所未有的数据多样性。本文将介绍一个为这些数据量身定制的解析系统,该系统结合了策略模式和规则引擎,旨在提供一个灵活、高效且可扩展的数据处理平台,以应对不断增长的数据处理需求。

2. 项目背景

在遥感技术和地球观测领域,处理和分析多源异构原始影像数据已成为一项至关重要的任务。随着卫星技术的不断发展,来自不同传感器、不同平台的原始影像数据呈现出多样化和复杂化的特点。这些数据包括但不限于光学影像、雷达影像、高光谱影像等,每种类型都有其独特的数据结构和元数据信息。我之前博客针对不同各类原始影像数据格式和元数据信息进行过详细梳理:【GIS系列】卫星遥感影像简介及格式梳理-CSDN博客

有效地解析和管理这些多源异构的原始影像数据,对于地质调查、环境监测、城市规划、农业管理等多个领域都具有重要意义。然而,由于数据来源的多样性和复杂性,传统的数据处理方法往往难以满足需求。

本文将介绍一个专门针对多源异构原始影像数据设计的解析系统,探讨如何构建一个灵活、高效、可扩展的数据处理平台。本文仅对如何解析多源异构数据提供解决方案思路,不无偿提供源码。

3. 系统现状与挑战

目前,在处理多源异构原始影像数据时,我们面临以下挑战:

1. 数据格式多样性:不同卫星、不同传感器产生的原始影像数据格式各不相同,包括但不限于GeoTIFF、HDF、NITF等。

2. 元数据结构差异: 各类影像的元数据结构存在显著差异,如何统一解析和存储这些元数据是一大难题。

3. 数据量巨大:遥感影像数据通常体积庞大,如何高效处理和存储是一个挑战。

4. 实时性要求:某些应用场景(如灾害监测)需要近实时的数据处理能力。

5. 扩展性需求:随着新型传感器和卫星的不断发布,系统需要能够快速适应新的数据类型。

6. 质量控制: 原始影像数据可能存在噪声、畸变等问题,需要在解析过程中进行初步的质量评估。

为了应对这些挑战,我们设计了一个基于策略模式和规则引擎的多源异构原始影像数据解析系统。这个系统具有高度的灵活性和可扩展性,能够有效处理各种复杂的原始影像数据。

4. 核心技术实现

多源异构原始影像解析入库流程图如下:

【GIS系列】多源异构原始影像解析:策略模式与规则引擎的应用-LMLPHP

上述流程图从前端、后端及数据库三个角度来呈现原始影像入库流程,首先由前端将文件路径传递给后台,后台基于规则引擎将影像映射到策略工厂,找到影像解析对应类,之后由解析类中的方法解析元数据,若解析成功则将元数据写入数据库同时写入成功日志流程结束,若解析失败则写入失败日志流程结束。 下文将介绍解析流程中涉及的核心技术栈。

4.1. 策略模式

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换,让算法的变化独立于使用算法的客户。客户端代码通过持有一个策略接口的引用来使用特定的算法,在运行时可以动态地切换不同的算法。

4.1.1. 多源异构影像解析策略模式部分核心代码

在多源原始影像元数据解析中引入了策略模式,前端只需要输入文件路径,根据策略模式建立的路由后台即可动态解析不同类型影像并写入数据库。

策略模式的路由策略主要在策略工厂中定义路由Map,以Map键值对的形式实现:

private static  Map<String, Function<File,Boolean>> handleMap = null;
@PostConstruct
public void init(){
    handleMap=new HashMap<>();
    handleMap.put("GF1B_PMS", originalImageFile -> gf1BPMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF1C_PMS", originalImageFile -> gf1CPMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF1D_PMS", originalImageFile -> gf1DPMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF1_PMS1", originalImageFile -> gf1PMS1HandleService.saveMetadata(originalImageFile));
    handleMap.put("GF1_PMS2", originalImageFile -> gf1PMS2HandleService.saveMetadata(originalImageFile));
    handleMap.put("GF6_PMS", originalImageFile -> gf6PMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF6_WFV", originalImageFile -> gf6WFVHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF2_PMS1", originalImageFile -> gf2PMS1HandleService.saveMetadata(originalImageFile));
    handleMap.put("GF2_PMS2", originalImageFile -> gf2PMS2HandleService.saveMetadata(originalImageFile));
    handleMap.put("BJ3B1_PMS", originalImageFile -> bj3B1PMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF5_AHSI", originalImageFile -> gf5AHSIHandleService.saveMetadata(originalImageFile));
    handleMap.put("LT5_TM", originalImageFile -> lt5TMHandleService.saveMetadata(originalImageFile));
    handleMap.put("LT05_TM", originalImageFile -> lt05TMHandleService.saveMetadata(originalImageFile));
    handleMap.put("LC08_OLI", originalImageFile -> lc08OLIHandleService.saveMetadata(originalImageFile));
    handleMap.put("LC09_OLI", originalImageFile -> lc09OLIHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF701_BWD", originalImageFile -> gf701BWDHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF701_MUX", originalImageFile -> gf701MUXHandleService.saveMetadata(originalImageFile));
    handleMap.put("ZY1E_VNIC", originalImageFile-> zy1EVNICHandleService.saveMetadata(originalImageFile));
    handleMap.put("zy302a_mux", originalImageFile-> zy302MUXHandleService.saveMetadata(originalImageFile));
    handleMap.put("zy302a_nad", originalImageFile -> zy302NADHandleService.saveMetadata(originalImageFile));
    handleMap.put("HJ2B_CCD4", originalImageFile -> hj2BCCD4HandleService.saveMetadata(originalImageFile));
    handleMap.put("BJ3N1", originalImageFile -> bj3N1PMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("TRIPLESAT_1_PMS",
            originalImageFile -> triplesat1PMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("JL1GF02A_PMS2", originalImageFile -> jl1GF02APMS2HandleService.saveMetadata(originalImageFile));
    handleMap.put("JL1KF01C_PMSR1",
            originalImageFile -> jl1KF01CPMSR1HandleService.saveMetadata(originalImageFile));
    handleMap.put("DP01_PMS", originalImageFile -> dp01PMSHandleService.saveMetadata(originalImageFile));
    handleMap.put("CB04A_WPM", originalImageFile -> cb04AWPMHandleService.saveMetadata(originalImageFile));
    handleMap.put("S1A_SAR", originalImageFile -> s1ASARHandleService.saveMetadata(originalImageFile));
    handleMap.put("S2A_SAR", originalImageFile -> s2ASARHandleService.saveMetadata(originalImageFile));
    handleMap.put("S2B_SAR", originalImageFile -> s2BSARHandleService.saveMetadata(originalImageFile));
    handleMap.put("GF3_SYC",originalImageFile->gf3SARHandleService.saveMetadata(originalImageFile));
}

在handleMap中以影像文件名的部分为键值,真实解析方法为map值,以达到用文件名作为路由的目的。 

public void saveMetadata(String imageType, File originalImageFile){
    handleMap.get(imageType).apply(originalImageFile);
}

 直接引用策略工厂的saveMetadata方法即可实现根据文件路径解析不同类型影像。

4.1.2. 策略模式的意义

多源异构影像解析中,我们采用的Map映射的方式,用Map键代表不同路由策略,以上两种设计均使用了策略模式的思想,具体体现在以下几个方面:

  1. 独立的路由策略实现: 每个枚举值对应一种具体的任务路由策略,这样可以将不同的路由策略实现封装到不同的类中,实现了算法的独立性。
  2. 扩展性: 可以轻松地向系统中添加新的路由策略,只需添加对应的枚举值和相应的路由策略实现类。这样做符合开闭原则,使得系统更容易扩展。
  3. 动态切换策略: 策略模式使得在运行时可以动态地切换任务路由策略,而不需要修改调度系统的核心代码。这增加了系统的灵活性。
  4. 清晰的接口定义: 通过策略模式,可以为每种路由策略定义清晰的接口,包括了选择执行器的逻辑。这样,不同的路由策略实现可以按照相同的接口定义来实现。

在项目中,策略模式的运用使得任务的路由策略更加灵活和可扩展,使得系统能够适应不同的业务场景和调度需求。

4.2. 规则引擎

规则引擎是一种软件系统或组件,专门设计用于管理和执行事先定义的业务规则。这些规则通常以条件-动作的形式存在,即当某些条件得到满足时,触发相应的动作。规则引擎通过将业务规则从应用程序中分离出来,提供了更灵活、易于维护的业务逻辑管理方式。网上查询了很多规则引擎框架,最终选择了Easy Rules,原因就是Easy Rules基于java写的,而且 设计简单,仅依赖少量的核心类库,易于集成和使用。

4.2.1. 规则引擎部分核心代码

在上一节中介绍了策略模式的概念以及多源原始影像解析功能中策略模式的使用细节,在策略模式中基于路由解析了不同类型影像写入数据库中,但还缺少一个桥梁,规则引擎正是作为桥梁将文件名称与策略模式的路由做了相关映射,使得多源原始影像元数据解析功能能完整串接起来。

1. 定义规则

我们需要根据不同类别的影像定义不同的规则,下述代码为高分1,2,5,6类别影像的规则:

@Rule(priority = 2)
public class GFRule {

    @Condition
    public boolean isGFImage(@Fact("fileFact") FileFact fileFact) {
        return "GF".equalsIgnoreCase(fileFact.getSplit()[0].substring(0, 2));
    }

    @Action
    public void saveMetadata(@Fact("fileFact") FileFact fileFact) {
        String[] split = fileFact.getSplit();
        fileFact.saveMetadata(split[0] + "_" + split[1], new File(fileFact.getFilePath()));
    }
}

@Condition注解标记计算规则条件的方法,return "GF".equalsIgnoreCase(fileFact.getSplit()[0].substring(0, 2))代码为截取文件名如为GF则符合规则。

@Action注解标记要执行规则操作的方法,fileFact.saveMetadata(split[0] + "_" + split[1], new File(fileFact.getFilePath()))代码为执行相应解析影像方法。

2. 定义事实

@Data
@AllArgsConstructor
public class FileFact {
    // 文件路径
    private String filePath;
    private String[] split;
    private ImageFactory imageFactory;

    public FileFact(String filePath, ImageFactory imageFactory) {
        this.filePath = filePath;
        this.imageFactory = imageFactory;
        File file = new File(filePath);
        split = file.getName().split("_");
    }

    public void saveMetadata(String imageType, File originalImageFile ) {
        imageFactory.saveMetadata(imageType,originalImageFile);
    }
}

3. 定义规则引擎调用组件

@Service
@Import(LTRule.class)
public class RuleService {
  //注入影像解析策略工厂
    @Resource
    ImageFactory imageFactory;

    private DefaultRulesEngine rulesEngine = null;
    private Rules rules = null;

    @PostConstruct
    public void init() {
        // 创建规则引擎,设置引擎参数 告诉引擎规则被触发时跳过后面的规则
        RulesEngineParameters parameters = new RulesEngineParameters()
                .skipOnFirstAppliedRule(true);
        rulesEngine = new DefaultRulesEngine(parameters);
        // 注册规则
        rules = new Rules();
        rules.register(new ZYRule());
        rules.register(new GFRule());
        rules.register(new GF7Rule());
        rules.register(new GF3Rule());
        rules.register(new LTRule());
        rules.register(new LT05Rule());
        rules.register(new LCRule());
        rules.register(new BJ3B1Rule());
        rules.register(new BJ3N1Rule());
        rules.register(new TripleSat1Rule());
        rules.register(new JL1AndDP01Rule());
        rules.register(new HJ2BRule());
        rules.register(new SARule());
    }


    public void matchRule(String filePath) {
        // 创建事实对象
        Facts facts = new Facts();
        FileFact fileFact = new FileFact(filePath, imageFactory);
        facts.put("fileFact", fileFact);
        // 开始映射
        rulesEngine.fire(rules, facts);
    }

}

这段代码定义了一个名为 RuleService 的服务类,使用 Spring 框架的特性进行配置。它初始化一个规则引擎,注册了一系列特定的规则来处理不同类型的影像文件。当传入一个文件路径时,这个服务通过匹配的规则动态决定如何解析文件并处理相关的元数据,使得系统能够灵活地应对各种遥感影像数据的处理需求。 

 5. 延展应用

虽然本文主要讨论了多源异构原始影像数据的解析,但这种基于策略模式和规则引擎的方法同样可以应用到其他相关领域:

1. 多源遥感数据融合:将不同类型的遥感数据(如光学和雷达数据)进行融合分析。

2. 历史影像数据处理: 处理和整合不同时期、不同来源的历史影像数据。

3. 实时遥感监测系统:构建能够实时接收和处理多源遥感数据的监测系统。

4. 卫星地面站数据管理: 管理和处理来自多个卫星地面站的数据流。

5. 遥感大数据分析平台:构建能够处理海量多源遥感数据的大数据分析平台。

这种方法为处理复杂的多源异构原始影像数据提供了一个灵活且可扩展的框架,也为其他类型的多源异构数据处理提供了有价值的参考。

 6. 结语

策略模式和规则引擎组合可以形成一套方法论用来解决多源异构数据解析的场景,此后有类似需求场景也可以使用这套方法论。若本文对你有启发和帮助,别忘记点点关注哦~

【GIS系列】多源异构原始影像解析:策略模式与规则引擎的应用-LMLPHP

08-31 14:24