非科班Java出身GISer

非科班Java出身GISer

Openlayers 教程 - 基于 Openlayers api 实现空间查询(客户端):点选、范围查询

客户端空间查询

在地理信息系统中,空间查询有的非常重要的作用,几乎所有地图相关的业务系统都需要空间查询

空间查询一般指:基于空间数据的查询,一般包括点选、线段查询、多边形查询、缓冲分析、范围查询等。

由于空间数据的特殊原因,空间查询往往基于服务端系统,通过数据库进行查询,构建索引之后,查询速度会有很大的提升。

但是对于一些简易的地图业务系统,不依赖空间数据库,因此不能使用服务端的空间查询。

在之前的业务系统中,遇到过类似的空间查询场景,解决办法一般是使用 turf 或者 jsts 来实现客户端空间查询;

昨天偶然发现 Openlayers 的矢量图层(VectorSource)自带一些空间查询方法,可以实现简单的空间查询。

Openlayers 可以实现的空间查询:点选查询和范围查询。

本文包括两部分。


核心代码

1. 应用函数

获取范围内图形要素

获取范围内图形要素(回调函数)

获取坐标点相交的图形要素

获取坐标点最近的图形要素

2. 创建随机数据,包括点线面数据。

// 默认范围
const bbox = [115.44400430633583, 39.20776490257249, 117.44351602508583, 40.84472779319749];

let number = 30;
// 模拟随机点数据
const points = turf.randomPoint(
    // 模拟数量
    number,
    {
        // 范围
        bbox: bbox
    })

// 模拟随机线段数据
const lineStrings = turf.randomLineString(
    // 模拟数量
    number,
    {
        // 范围
        bbox: bbox,
        // 顶点数量
        num_vertices: 10,
        // 最大长度
        max_length: 0.1,
        // 最大角度
        max_rotation: Math.PI / 8
    })

// 模拟随机多边形
const polygons = turf.randomPolygon(
    // 模拟数量
    number,
    {
        // 范围
        bbox: bbox,
        // 最大辐射长度
        max_radial_length: 0.1,
        // 顶点数量
        num_vertices: 10
    })

const randomObject = {
    'Point': points,
    'LineString': lineStrings,
    'Polygon': polygons,
}

// 清空数据,添加数据
layerQuery.getSource().clear();
layerQuery.getSource().addFeatures(getFeatureByGeoJson(randomObject.Point));
layerQuery.getSource().addFeatures(getFeatureByGeoJson(randomObject.LineString));
layerQuery.getSource().addFeatures(getFeatureByGeoJson(randomObject.Polygon));

  1. 创建标绘事件。

注意:这里的事件类型只有 PointExtent,并且 Extent 需要 Circleol.interaction.Draw.createBox(4)

let geometryFunction = ol.interaction.Draw.createBox(4);

drawObject = new ol.interaction.Draw({
    source: layer.getSource(),
    type: type,
    geometryFunction: type === 'Circle' ? geometryFunction : undefined,
});

3. 判断是否相交。

这里需要注意的是,由于点选是点数据的原因,选中点数据和线段数据的难度比较大,因此这里使用的是获取最近的数据。

而且,判断只留下 200 米以内的数据;这里的判断也可以根据 zoom 值来调整,比如地图等级 10 级的时候设置 500 米以内。

// 确定容差范围:200米
function available(feature1, feature2) {
    const line = new ol.geom.LineString(
        [feature1.getGeometry().getCoordinates(),
            feature2 instanceof Array ? feature2 : feature2.getGeometry().getCoordinates()]);
    const len = ol.sphere.getLength(line, {projection: 'EPSG:4326'});
    return len <= 200;
}

// 点选查询
if (e.feature.getGeometry().getType() === 'Point') {

    // 获取最近的图形要素,这里给的范围为 200米以内的数据
    feature = layerQuery.getSource().getClosestFeatureToCoordinate(
        e.feature.getGeometry().getCoordinates());

    // 点和线可以允许容差,多边形需要再内部才认为查询到
    switch (feature.getGeometry().getType()) {
        case 'Point':
            if (!available(e.feature, feature)) {
                feature = undefined;
            }
            break;
        case 'LineString':
            // getFeaturesAtCoordinate
            const point = feature.getGeometry().getClosestPoint(e.feature.getGeometry().getCoordinates());

            if (!available(e.feature, point)) {
                feature = undefined;
            }
            break;
        case 'Polygon':
            if (!feature.getGeometry().intersectsCoordinate(e.feature.getGeometry().getCoordinates())) {
                feature = undefined;
            }
            break;
    }

    // 改变样式
    feature && feature.setStyle(selectedStyle);

// 范围查询
} else {

    // 查询范围内数据
    feature = layerQuery.getSource().getFeaturesInExtent(e.feature.getGeometry().getExtent());
}
            

在线示例

专门为地图开发人员准备的地图工具:在线地图工具

Openlayers api 实现空间查询:OpenLayers space query

Openlayers 教程 - 基于 Openlayers api 实现空间查询(客户端):点选、范围查询-LMLPHP

06-05 11:56