Skip to main content

第八节:使用自定义地图工具实现任意点预报

有了上一节的铺垫,我们这一节开始实现任意点预报功能。

功能分析

任意点预报的流程较为清晰:

如果我们直接在切换地图工具的方法里面编写这个逻辑,那么后续其他地图工具的逻辑就会都在一起,这样代码会比较乱,因此我们采用QE中规范的地图工具来进行实现。

使用标准地图工具,完善切换逻辑

QE默认提供了绘制工具,虽然也支持点的绘制,但是这里我们为了演示自定义绘制工具的使用,将上面这个简单的逻辑封装为一个标准的地图工具。

既然使用标准地图工具,那么我们可以先完善switchTool方法的逻辑来支持不同地图工具之之间的切换:

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

创建自定义地图工具

我们通过自定义地图工具来实现上面规划的功能。

实现地图工具接口

QE中的地图工具使用IMapTool接口定义,因此我们创建一个PointForecastMapTool类来实现该接口:

class PointForecastMapTool extends Evented implements IMapTool {

    protected map: LMap;

    public begin(properties?: any) {
    }

    public end() {
        this.clear();
    }

    public clear() {
    }

    public setMap(map: any) {
        this.map = map;
    }
}

以上就是一个空地图工具,当调用map.toolService.setCurrent时,传入的工具会被激活,首先是setMap方法被调用,框架将绑定的地图传递给工具,然后是begin被调用,说明这个工具进入激活状态。

如果此时地图上已经有地图工具被激活,QE会首先调用上一个工具的end方法来结束上一个地图工具的激活状态。所以,在end方法里面,可以根据实际需要来决定是否清空地图上的绘制。

在工具被激活时监听地图点击事件

在begin方法中进行监听,然后设置一个函数进行响应:

protected handleMapClick(args: L.LeafletMouseEvent) {
    
}

public begin(properties?: any) {
    this.map.on("click", this.handleMapClick, this);
}

根据点击信息获取预报信息显示

在这里,我们需要在用户点击的地方显示一个marker,然后开始请求数据,最后将请求的结果以popup形式呈现。

显示marker

我们首先在工具内部定义一个私有的marker对象,这样第一次使用的时候创建,后续只要更新marker的位置即可,当然也可以每次都先删除之前的marker后重新创建,没有太大的区别。

private _marker: L.Marker;

更新handleMapClick方法:

if (!this._marker) {
    this._marker = L.marker(args.latlng).addTo(this.map);
} else {
    this._marker.setLatLng(args.latlng);
}

到目前为止,我们已经实现了点击后能在地图上显示一个marker,现在我们把这个工具真正的关联到业务逻辑中,更新工具管理器的switchTool方法:

public switchTool(value: string) {
    ...
    let mapTool: IMapTool;
    if (value === "forecast") {
        mapTool = new PointForecastMapTool();
    }
  	...
}
在云平台中,Leaflet的资源如果不是使用的在线的cdn,就需要在代码最开始的地方加入资源位置设置的代码,主要就是指定默认marker图片从哪里获取,如果您没有使用默认的marker图片,而是自己指定了icon,也可以不设置。使用webpack脚手架开发无需考虑此问题。

指定Leaflet默认图片资源的方式如下:

L.Icon.Default.imagePath = "https://unpkg.com/leaflet@1.7.1/dist/images/";

这时候,如果点击任意点预报,我们就可以在地图上画点了:

监测预报-第八节-地图打点.png

获取预报数显示

在本节中,我们使用晴天钟提供免费的任意点预报数据接口,由于仅用来演示,我们直接使用了能够直接获取图片的接口,这样拿回来的图片我们可以直接显示。

接口访问地址如下:

https://www.7timer.info/bin/civillight.php?lon=${args.latlng.lng}&lat=${args.latlng.lat}&lang=zh-CN&output=internal&tzshift=0
该接口数据基于GFS预报,仅供学习使用,获取天气预报请使用气象局官方预报结论!

其中经纬度由用户传入,返回的是一张如下的图片:

监测预报-第八节-任意点预报结果.png

我们将响应点击事件的方法进行更新,以便能够根据点击的位置请求接口,然后把获取到的图片在popup中显示:

protected handleMapClick(args: L.LeafletMouseEvent) {
    if (!this._marker) {
        this._marker = L.marker(args.latlng).addTo(this.map);
    } else {
        this._marker.setLatLng(args.latlng);
    }
    const imgUrl = `https://www.7timer.info/bin/civillight.php?lon=${args.latlng.lng}&lat=${args.latlng.lat}&lang=zh-CN&output=internal&tzshift=0`;
    const currentId = uid();
    this._loadingId = currentId;
    message.info({ duration: 10000, content: "数据请求中..." });
    const img = new Image();
    img.onload = () => {
        if (this._loadingId !== currentId) {
            //已经有二次点击
            return;
        }
        map.openPopup(img, args.latlng, { maxWidth: img.width });
        message.destroy();
    };
    img.onerror = () => {
        message.error("该点暂无数据!");
        message.destroy();
    };
    img.src = imgUrl;
}
在上面的代码中,我们增加了一个私有变量_loadingId,用来标记当前的点击,因为用户有可能在数据还没有返回之前就点击下一个点,这时候如果数据返回了,我们需要取消后续操作。

清理地图绘制内容

当我们切换到其他地图工具时,我们需要清理当前地图上绘制的内容(如希望保留,也可以不清理),这可以通过在end方法中调用clear方法实现,clear方法是地图工具的标准方法,清理的代码应该写在此,但是默认工具被取消激活时并不会调用clear方法,所以如有需要,可以在end方法中主动调用:

public end() {
    this.map.off("click", this.handleMapClick, this);
    this.clear();
}

public clear() {
    this._marker && this._marker.remove();
    this._marker = undefined;
    this.map.closePopup();
}

现在点击地图,就可以出现任意点预报结果了,切换到其他工具,当前的内容也会及时清除,至此,我们的任意点预报功能就完整的实现了!

总结

本节主要介绍了自定义地图工具的创建,然后结合免费的任意点预报接口实现了任意点预报的显示,现在您可以根据自己的需要定制其他地图交互工具了,下一节我们将会介绍QE内置的一个功能丰富的矢量绘制工具!

完整代码和效果