Skip to main content

自定义格点数据解析器

当框架内置的格点数据解析器满足不了您的数据格式的时候,您也可以针对您的数据自定义格点数据解析器来进行扩展。

格点数据解析器基类

QE中格点数据解析器的基类是 GridDataProviderBase ,我们自定义的格点数据解析器应继承该抽象类 GridDataProviderBase

我们先看下 GridDataProviderBase 中的属性和方法:

格点属性信息

gridOptions 中存储的是格点属性信息,因为数据是要在地图上进行表达,所以网格数据需要具备地理信息属性,其接口是 IGridDataOptions。我们自定义的格点数据解析器中也需要使用gridOptions来描述我们的格点数据。

格点场

GridDataProviderBase 类中 grids 表示解析器中所有维度数据,其接口是 GridData[][],数据区域支持四个维度,其中时间在最外层,接下来是层次,然后是纬度,最后是经度。其中时间和层次可以只有一个时次和一个层次。GridData表示单个二维格点场数据。所以我们自定义的格点数据解析器中需要按照这种格式将数据存进 grids

  • 获取所有维度数据

GridDataProviderBase 类中提供了 allGrids方法来获取所有维度数据,返回值类型是GridData[][]

  • 获取单个二维格点场

GridDataProviderBase 类中提供了 getGrid方法来获取一个二维格点场,返回值类型是GridData

  • 更新格点数据

GridDataProviderBase 类中提供了 updateGrid 方法来更新grids中的格点数据。我们在做数据动画的边载入边播放时就是通过该方法不断更新里面的值。

当前时间维度值

GridDataProviderBase 类中使用 currentTIdx 表示数据源中当前生效的时间索引号。我们数据源时间动画就是不断改变这个值。

当前高度维度值

GridDataProviderBase 类中使用 currentZIdx 表示数据源中当前生效的高度索引号。数据源高度动画就是不断改变currentZIdx这个值。

事件监听

GridDataProviderBase 类中提供了一系列注册监听数据源时间索引号和高度索引号发生变化的回调事件方法。当在时间索引号和高度索引号发生变化而需要进行一些操作时,可以使用这类方法来注册回调函数。

  • onTChangedoffTChanged

监听和取消监听时间索引号发生变化的消息

  • onZChangedoffZChanged

监听和取消监听高度层索引号发生变化的消息

  • onTZChangedoffTZChanged

监听和取消监听时间和高度索引号发生变化的消息

  • onIntTChangedoffIntTChanged

监听和取消监听时间的整数部分发生变化的消息

  • onIntZChangedoffIntZChanged

监听和取消监听高度的整数部分发生变化的消息

  • onIntTZChangedoffIntTZChanged

监听和取消监听时间和高度的整数部分发生变化的消息

  • onActiveGridUpdatedoffActiveGridUpdated

监听和取消监听当前时间和高度索引所在的格点发生变化的消息

自定义格点数据解析器

我们以解析geotiff数据为例,自定义一个解析geotiff数据的格点解析器。在继承GridDataProviderBase这个类的前提下,实现一个自定义格点数据解析器,只需实现gridOptionsgrids

首先我们使用geotiff.js库来解析(Geo)TIFF 文件,读取格点属性信息和格点场原始数组数据。

private static readTifBuffer = async(buffer: ArrayBuffer) => {
  const tiff = await GeoTIFF.fromArrayBuffer(buffer);
  const image = await tiff.getImage();
  const reso = image.getResolution() as number[];
  const origin = image.getOrigin() as number[];
  const width = image.getWidth();
  const height = image.getHeight();
  const options = {
    xStart: origin[0],
    yStart: origin[1],
    xDelta: reso[0],
    yDelta: reso[1],
    xSize: width,
    ySize: height
  }
  const data = await image.readRasters();
  return {options, data}
}

接下来实现基类中的 gridOptions。刚方法中我们已经组装了options,这里只需赋值给gridOptions,然后通过 ensureGridDataOptions 方法检查格点属性,确保有效。

然后创建数据区域 grids通过实例化一个 GridData 来创建单个二维格点场数据,这样会整块解析二进制数据,而不是逐点读取写入。

创建GridData时需传入如下构建参数:

  • 格点数据类型

这里我们根据数据的构造函数名来获取格点数据类型

private getDataType = (data: ArrayBuffer) => {
  switch (data.constructor.name) {
    case "Int8Array":
      return GridDataType.Int8;
    case "Uint8Array":
      return GridDataType.UInt8;
    case "Int16Array":
      return GridDataType.Int16;
    case "Uint16Array":
      return GridDataType.UInt16;
    case "Int32Array":
      return GridDataType.Int32;
    case "Uint32Array":
      return GridDataType.UInt32;
    case "Float32Array":
      return GridDataType.Float32;
    case "Float64Array":
      return GridDataType.Float64;
    default:
      throw new Error("不支持的数据类型:" + data.constructor.name);
  }
}
  • x长度,y长度

就是 gridOptions 中的 xSizeySize

  • 数据信息

前面 readTifBuffer 方法中得到的data数组中第一个元素就是图片中解析出的数据信息。

  • 缺测值

缺测值我们外部传入,不传则使用默认缺测值。

创建 gridOptionsgrids 的代码如下:

private load = (data: ArrayBuffer, options: ITifGridDataProviderOptions) => {
  this.gridOptions = options.gridOptions;
  ensureGridDataOptions(this.gridOptions);
  let levelData: GridData[] = [];
  const dataType = this.getDataType(data);
  const undef = options.undef ? options.undef : consts.defaultUndef;
  const gridData = new GridData(dataType, this.gridOptions.xSize, this.gridOptions.ySize, data, undef);
  levelData.push(gridData);
  this.grids.push(levelData);
}

因为解析geotiff数据的过程是异步的,我们再定义一个方法来创建该格点数据解析器

public static fromBuffer = async (buffer: ArrayBuffer, options: ITifGridDataProviderOptions = {} as any): Promise<TifGridDataProvider> => {
  return TifGridDataProvider.readTifBuffer(buffer).then(res => {
    options.gridOptions = options.gridOptions || res.options;
    const provider = new TifGridDataProvider(res.data[0], options);
    return provider;
  }).catch(err => {
    debugger
  }) as any;
}

最后我们看下,是如何使用这个自定义的格点数据解析器的

const creatLayer = async () => {
    message.info("数据加载中...", 0);
    const buffers = await getBinary("public/demos/data/tif/sanxia.tif");
    message.destroy();
    message.info("数据解码中...", 0);
    const provider = await TifGridDataProvider.fromBuffer(buffers[0], { undef: 65535 });
    message.destroy();
    const maxMin = provider.getGrid().maxMin;
    const colorScale = await getPredefinedBitmapScale(
        predefinedLegendNames.BkBlAqGrYeOrReViWh200,
        maxMin.min,
        maxMin.max,
        true
    );
    const layer = new LPixelLayer().setDataSource(provider).setDrawOptions({
        colorScale: colorScale,
        fillMode: GridDataGLFillMode.pixel1,
    })
    map.addLayer(layer)
}

完整示例