React Leaflet Esri 交互式空间查询

6 篇文章 0 订阅
6 篇文章 0 订阅

任务准备

  1. 安装AntD并实现按需加载
  2. 安装esri-leaflet和leaflet

具体任务

  1. 主组件中加载一个矢量地图并确定三个功能按钮:
    1. 清除选区:移除在当前地图上已经绘制的选区
    2. 圈选查询:在地图上以鼠标交互方式绘制圆形选区,并利用绘制结果进行空间查询
    3. 多变形查询:在地图上以鼠标交互方式绘制多边形选区,并利用绘制结果进行空间查询
  2. 查询结果以AntD-drawer组件为容器,展示结果的属性表

运行效果

在这里插入图片描述

基本代码

组件css

#map_interactspatialquerytask {
    width: 100%;
    height: 800px;
    position: relative;
}

.toolbar {
    position: absolute;
    top: 50px;
    right: 50px;
}

组件jsx

import React, { useEffect, useState } from 'react';
import L from "leaflet";
import * as esri from "esri-leaflet"
import { Button } from 'antd';
import PropertiesTable  from "../testPopup/PropertiesTable";
import DrawCircleGraphHandler from './DrawCircleGraphHandler';
import DrawPolygonGraphHandler from "./DrawPolygonGraphHandler";

import "./InteractSpatialQueryTask.css"

function InteractSpatialQueryTask(props) {

    const [layer, setLayer] = useState(undefined);
    const [tablevisuable, setTablevisuale] = useState(undefined);
    const [features,setFeatures] = useState(undefined);
    const [circleHandler, setCircleHandler] = useState(undefined);
    const [polygonHandler, setPolygonHandler] = useState(undefined);

    useEffect(()=>{
        let map = L.map("map_interactspatialquerytask", {crs : L.CRS.EPSG3395}).setView([29.779, 106.384],12)
        let layer = esri.featureLayer({url: "http://localhost:6080/arcgis/rest/services/02_Tky_GDCGK/FeatureServer/4", 
                pointToLayer: (geojson, latlng)=> {
                    return L.circleMarker(latlng, {radius : 5, color:"#567495"})
                }})
        layer.addTo(map)
        setLayer(layer)
        //创建交互绘制帮助类
        let circleHandler = new DrawCircleGraphHandler(map)
        setCircleHandler(circleHandler)
        let polygonHandler = new DrawPolygonGraphHandler(map)
        setPolygonHandler(polygonHandler);
    },[])


    const _clear_selection = () => {
        //清除已有的绘制选区
        if(circleHandler)
            circleHandler.clearCircle()
        if(polygonHandler)
            polygonHandler.clearPolygon()
    }

    const _draw_cricle = () => {
        _clear_selection();
        //开启交互绘制,并指定结果选区的样式和回调
        circleHandler.drawCircle({color: '#ff0000', fillColor: '#558800', fillOpacity: 0.5}, (center,radius,circle)=>{
            //对于圈选范围,调用nearby进行查询
            layer.query().nearby(center, radius).run((error,results) => {
                //修改结果,并交由UI进行展示
                if(results.features){
                    setFeatures(results.features)
                    setTablevisuale(true)
                }
            })
        })
    }

    const _draw_ploygon = () => {
        _clear_selection();
        polygonHandler.drawPolygon({color: '#ff0000', fillColor: '#558800', fillOpacity: 0.5}, (vertex,polygon)=>{
            //对于圈选范围,调用within进行查询
            layer.query().within(polygon).run((error,results) => {
                if(results.features){
                    setFeatures(results.features)
                    setTablevisuale(true)
                }
            })
        })
    }

    const onTableClose = () => {
        setTablevisuale(false);
    }

    return (
        <div id = "map_interactspatialquerytask">
            <Button.Group className = "toolbar">
                <Button type = "dashed" onClick = {_clear_selection}>清除选择</Button>
                <Button type = "primary" onClick = {_draw_cricle}>圆形选择区</Button>
                <Button type = "primary" onClick = {_draw_ploygon}>多边形选择区</Button>
            </Button.Group>
            <PropertiesTable 
                visible = {tablevisuable} 
                features = {features} 
                closeCallback = {onTableClose}>
            </PropertiesTable>
        </div>
    );
}

export default InteractSpatialQueryTask;

AntD_Drawer实现的查询结果列表展示组件

import React from 'react';
import { Drawer, Table, Card } from 'antd';

function PropertiesTable(props) {

    const _generateColumns = (props) => {
        return Object.keys(props.features[0].properties).map((value, index) => {
            return {
                title: value,
                key: value,
                dataIndex: value,
            }
        })
    }

    const _generateContext = (props) => {
        return props.features.map((value, index) => { return { ...value.properties, key: index } })
    }

    const _drawTable = (props) => {
        if (props.features && props.features.length > 0) {
            return (
                <Table
                    size="small"
                    dataSource={_generateContext(props)}
                    columns={_generateColumns(props)}>
                </Table>
            )
        }
        else
            return <Card size="default" style={{ width: "100%" }}><p>没有数据</p></Card>

    }

    return (
        <Drawer
            onClose={() => { props.closeCallback() }}
            visible={props.visible}
            title="属性表"
            getContainer={true}
            placement="bottom">
            {_drawTable(props)}
        </Drawer>
    );
}

export default PropertiesTable;

点选查询帮助类


import L from "leaflet";

export default class DrawCircleGraphHandler {

    constructor(map) {
        this.map = map //当前地图
        this.resultAOI = undefined; //圈选选区
    }

    //鼠标按下后的事件监听,确定圈选的圆心并创建结果选区
    onmouseDown = (e, style) => {
        this.clearCircle();
        this.resultAOI = L.circle(e.latlng, style).addTo(this.map);
    }

    //鼠标抬起后的事件监听
    onmouseUp = (e, callback) => {
        //依据鼠标松开的位置计算圈选的半径
        this.resultAOI.setRadius(L.latLng(e.latlng).distanceTo(this.resultAOI.getLatLng()))
        //执行回调
        callback(this.resultAOI.getLatLng(), this.resultAOI.getRadius(), this.resultAOI)
        //移除绘制时特殊的监听
        this.map.dragging.enable();
        this.map.off('mousedown', this.onmouseDownDecore);
        this.map.off('mouseup', this.onmouseUpDecore);
        this.map.off('mousemove', this.onmouseMove)
    }

    //鼠标拖动时的事件监听:不断的刷新临时圈选图层
    onmouseMove = (e) => {
        //前提:结果选区已经存在
        if (this.resultAOI) {
            //计算圈选图层的半径,并修改圈选图层
            /******************************************************************************************
             * 补充点:对于已经添加到地图的图层,直接修改图层对象本身,就可以通知地图进行重绘,而不用反复重新添加
             ******************************************************************************************/
            this.resultAOI.setRadius(L.latLng(e.latlng).distanceTo(this.resultAOI.getLatLng()))
        }
    }

    //外部方法:清除结果选区
    clearCircle() {
        //直接移除图层
        if (this.resultAOI) {
            this.resultAOI.remove();
            this.resultAOI = undefined;
        }
    }

    drawCircle(style, callback) {
        //开始绘制,为了方便在回调时进行参数传递,这里对原有回调进行了外壳套接
        this.onmouseUpDecore = (e) => { this.onmouseUp(e, callback) }
        this.onmouseDownDecore = (e) => { this.onmouseDown(e, style) }
        //将mousemove事件移动地图禁用
        this.map.dragging.disable();
        this.map.on('mousedown', this.onmouseDownDecore);
        this.map.on('mouseup', this.onmouseUpDecore);
        this.map.on('mousemove', this.onmouseMove)
    }
}

多边形查询帮助类

import L from "leaflet";

export default class DrawPolygonGraphHandler {

    constructor(map) {
        this.map = map;
        this.resultAOI = undefined;
        this.tempGraphLayer = undefined;
        this.timer = undefined;
    }

    onClick = (e, style) => {
        //主要为了防止双击和单机事件有所冲突
        clearTimeout(this.timer)
        this.timer = setTimeout(() => {
            //如果结果选取已经存在,则追加修改结果选取的节点
            if (this.map.hasLayer(this.resultAOI))
                this.resultAOI.addLatLng([e.latlng.lat, e.latlng.lng])
            //如果结果选区不存在,就添加创建结果选区
            else
                this.resultAOI = L.polygon([[e.latlng.lat, e.latlng.lng]], style).addTo(this.map)
        })
    }

    onDblclick = (e, callback) => {
        //清除最后一次绘制的临时图层
        if (this.tempGraphLayer) {
            this.map.removeLayer(this.tempGraphLayer)
            this.tempGraphLayer = undefined;
        }
        //执行回调
        callback(this.resultAOI.getLatLngs()[0], this.resultAOI)
        //移除绘制时特殊的监听
        this.map.dragging.enable();
        this.map.off('click', this.onClickDecorate);
        this.map.off('dblclick', this.onmouseUpDecore);
        this.map.off('mousemove', this.onMoveDecorate)
    }

    onMove = (e, style) => {
        //结果选区中记录了所有的顶点信息,每次从中获取以便绘制新的临时选区
        if (this.resultAOI && this.resultAOI.getLatLngs() && this.resultAOI.getLatLngs().length > 0) {
            //构建新的临时选区的顶点数组,实际就是结果选区中的顶点在尾部追加当前的顶点
            let tempVertex = [...this.resultAOI.getLatLngs()[0], e.latlng]
            //如果结果选取已经存在,则整体修改临时选区的节点
            if (this.map.hasLayer(this.tempGraphLayer))
                this.tempGraphLayer.setLatLngs(tempVertex)
            //如果结果选区不存在,就添加创建临时选区
            else
                this.tempGraphLayer = L.polygon(tempVertex, style).addTo(this.map)
        }
    }

    clearPolygon() {
        if (this.resultAOI) {
            this.resultAOI.remove();
            this.resultAOI = null;
        }
    }

    drawPolygon(style, callback) {
        this.onDblclickDecorate = (e) => { this.onDblclick(e, callback) }
        this.onClickDecorate = (e) => { this.onClick(e, style) }
        this.onMoveDecorate = (e) => { this.onMove(e, style) }
        this.map.dragging.disable();
        this.map.on('click', this.onClickDecorate);
        this.map.on('dblclick', this.onDblclickDecorate);
        this.map.on('mousemove', this.onMoveDecorate)
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值