Skip to main content

第九节:绘制和测距工具接入

上一节中我们实现了自定义地图工具来做任意点预报,这一节我们将直接使用QE内置的地图工具,所以会更简单一些!

添加区域绘制功能

在本示例中,我们计划添加的是区域绘制功能,这是一个纯粹为了演示而演示的功能,但是业务系统的复杂功能往往是由这些小的功能组成。

功能分析

我们希望这个区域绘制具有如下功能:

  • 能够独立设置每次绘制的颜色
  • 能够将绘制好的图形持久化的保存在一个固定图层中,单独控制显示和隐藏

创建绘制工具

为了实现上面的功能,我们将创建绘制工具的方法抽为一个独立的方法_createDrawTool,然后在切换地图工具的方法中调用:

public switchTool(value: string) {
    ...
    let mapTool: IMapTool;
    if (value === "forecast") {
        mapTool = new PointForecastMapTool();
    } else if (value === "draw_poly") {
        mapTool = this._createDrawTool();
    } else if (value === "distance") {
    } else {
        message.warn("未能获取到对应的地图工具!");
        return;
    }
    map.toolService.setCurrent(mapTool ?? null);
}

private _createDrawTool() {
    return undefined;
}

接下来我们使用QE内置的LVectorMapTool作为绘制工具,这是一个功能丰富的绘制工具,可以绘制数十种图形,而且支持扩展,目前我们用来绘制曲线多边形,只要简单的创建并且返回即可:

private _createDrawTool() {
    const mapTool = new LVectorMapTool({ drawType: PlotTypes.CLOSED_CURVE, autoClear: true, continious: true });
    return mapTool;
}
在创建地图工具时,我们将autoClear设置为true,这样可以在绘制下一个图形的时候自动清空之前的绘制,continious为true表示连续绘制,如果设置为false,会绘制完后恢复为pan工具,如果希望这样的行为,还需要UI状态的配合,在这里我们设置为连续绘制。

如果我们不需要实现用户自定义绘制的颜色,那么只要autoClear为false,就可以实现绘制图层不消失,除非手动调用clear方法。

此时点击区域绘制按钮,我们就可以在地图上绘制多边形了:

监测预报-第九节-绘制工具.png

单独存储绘制内容,重设图形颜色

虽然绘制的时候我们都使用了同一种颜色,但是我们希望在绘制结束后保存的图形能够显示不同的颜色,在这里我们在外部定义一个独立的图层interactionLayer,并且把这个图层的数据源也单独保存为interactionDataSource,为了实现不同的颜色,使用一个interactionLayerColors来保存每次绘制图形后所定义的颜色。

定义代码如下:

const interactionLayerColors: { [key: string]: string | Spectra } = {};
const interactionLayerStyle: IFeatureStyleOptions = {
    polygon: {
        fill: true,
        color: (feature) => {
            return interactionLayerColors[feature.id] ?? "rgba(255,0,0,0.6)"
        }
    }
}
let interactionDataSource: GeoJSONFeatureProvider;
const interactionLayer = new LGeoJSONLayer({ pane: consts.customPanes.topmap.name }).setDrawOptions(interactionLayerStyle);

之所以在外部保存这个图层,是为了方便后面在菜单里面能够直接显示和隐藏这个图层,实际应用中可以使用图层id来直接从map中获取这个图层,这里只是为了方便才定义在外部。为了方便的实现每个对象独立定义颜色,绘制的color值使用函数从缓存的颜色map中获取用户定义的颜色,如果获取不到则使用默认颜色。

要实现绘制完成的图层存储的步骤如下:

  • 监听图元绘制完成的消息
  • 每次绘制完成后,调用绘制工具的getGeoJSON方法来获取当前有效的绘制结果
  • 获取绘制图元的id,如果不存在则创建
  • 要求用户输入一个颜色,如果不输入,则使用默认颜色
  • 将id作为key,颜色作为value存储到interactionLayerColors
  • 将绘制结果更新到图层数据源中
  • 重绘图层
  • 清空绘制的缓存

实现代码如下:

mapTool.on(LVectorMapTool.EventTypes.drawFinish, args => {
    const dataSource: GeoJSON.FeatureCollection<GeoJSON.Polygon> = mapTool.getGeoJSON() as any;
    const feature = dataSource.features[0];
    const color = prompt("请填入颜色,支持css颜色,若使用默认颜色直接点击确定");
    if (color) {
        feature.id = feature.id ?? uid();
        const sColor = new Spectra(color);
        if (sColor.alpha() === 1) {
            sColor.alpha(0.6);
        }
        interactionLayerColors[feature.id] = sColor;
    }
    if (!interactionDataSource) {
        interactionDataSource = new GeoJSONFeatureProvider(dataSource);
        interactionLayer.setDataSource(interactionDataSource);
    } else {
        interactionDataSource.addFeature(feature);
        interactionLayer.redraw();
    }
    mapTool.clear();
});

这时候我们绘制完发现图层被清空了,并没有加到地图上,这是因为我们的交互图层还没有添加到地图,我们可以直接在创建的地方加到地图上,但是这样就会一直在地图上,我们选择另一种方式,将交互图层设置为图层菜单,可以由用户控制是否显示,我们在图层管理器的菜单配置中加入如下配置:

{
    name: "交互图层",
    id: "interaction",
    userData: {
        overlay: true
    }
},

然后还要在创建图层的地方返回这个交互图层,这样才能将交互图层接入我们的业务逻辑:

public async createLayer(item: IMenuItem): Promise<boolean> {
    ...
    if (item.id === "realtime_metar") {
         layer = await this._createMetarLayer();
     } else if (item.id === "realtime_sate") {
         layer = this._createCloudLayer();
     } else if (item.id.startsWith("gfs")) {
         layer = await this._createGFSLayer(item.userData.dataInfo);
     } else if (item.id === "flight") {
         layer = await this._createFlightLayer();
     } else if (item.id === "interaction") {
         layer = interactionLayer;
     }
    ...
}

接着设置这个图层是默认加载到地图的:

public defaultSelectedItemIds = ["interaction", "realtime_metar", "flight"];

现在,点击绘制按钮,可以不断的绘制新的图形,而且可以支持很多不一样的颜色:

监测预报-第九节-绘制多个图形.png

我们的绘制工具接入完成!

实现测距

QE内置了一个测距的地图工具LDistanceMapTool,只要直接使用即可,将切换地图工具的函数更新如下:

public switchTool(value: string) {
    if (!defined(value) || value === "pan") {
        map.toolService.getCurrent()?.clear();
        map.toolService.setCurrent(null);
        return;
    }
    let mapTool: IMapTool;
    if (value === "forecast") {
        mapTool = new PointForecastMapTool();
    } else if (value === "draw_poly") {
        if (!interactionLayer["_visible"]) {
            message.warn("当前交互图层被隐藏,绘制后将无法看!");
        }
        mapTool = this._createDrawTool();
    } else if (value === "distance") {
        mapTool = new LDistanceMapTool();
    } else {
        message.warn("未能获取到对应的地图工具!");
        return;
    }
    map.toolService.setCurrent(mapTool ?? null);
}

然后我们的测距工具就接入完成了: 监测预报-第九节-测距工具.png

如果您觉得不够好用,也可以按照我们上一节自定义工具中的介绍,自己定义一个测距工具!

总结

越看到最后会发现QE的使用很简单,更多的是对业务逻辑的把握,业务梳理清楚后,再有QE的加持,一个应用系统很快就能搭建出来!

完整代码和效果