当前位置: 首页 > article >正文

react-amap海量点优化

前言:高版本的react-amap 支持MassMarkers 组件用于一次性添加大量的标记点。本次优化的海量点是在低版本react-amap的基础上。官方推荐使用聚合useCluster属性来优化海量点的渲染。

直接附上代码:

import React, { Component } from "react";
import { Map, Markers, Polygon, InfoWindow } from 'react-amap'
import {
  Form,
  Switch,
  message,
} from "antd";
import ActionsForm from "components/RoutePanel/ActionsForm";
import { MAP_AMAP_KEY, MAP_AMAP_VERSION } from 'constants/config'
import {
  API_FETCH_ZONES_DEPT_POINT,
} from 'constants/api'
import fetch from 'common/fetch'
import styles from "./amap.css";
import { debounce } from 'lodash';

const polygonType = {
  main: 'main',
  adjacent: 'adjacent',
  normal: 'normal',
}

// 获取区域样式
const getPolygonStyle = (pt) => {
  switch (pt) {
    case polygonType.main: 
      return {
        fillColor: '#DC2626',
        fillOpacity: 0.5,
        strokeOpacity: 0.5,
        strokeWeight: 2,
        strokeColor: '#DC2626',
      }
    case polygonType.adjacent:
      return {
        fillColor: '#34D399',
        fillOpacity: 0.5,
        strokeOpacity: 1,
        strokeWeight: 1,
        strokeColor: '#34D399',
      }
    case polygonType.normal:
    default:
      return {
        fillColor: '#333333',
        fillOpacity: 0.3,
        strokeOpacity: 0.5,
        strokeWeight: 1,
        strokeColor: '#333333',
      }
  }
}

export class ZoneNeighborhoodMap extends Component {
  constructor(props) {
    super(props)

    this.map = null; // 地图实例
    this.mapEvents = {
      created: (mapInstance) => {
        this.map = mapInstance;
        this.map.on('zoomend', this.handleZoom);
      },
      close: () => {
        this.map = null;
        if (this.map) {
          this.map.off('zoomend', this.handleZoom);
        }
      }
    };

    this.state = {
      infoWindowPosition: null,
      infoWindowTitle: '',
      polygonActive: false,
      pointReferShow: true, //运输点参考
      pointReferData: [],
      areaReferShow: true, //已绘制参考
      areaReferData: [],
      locTypes: [],
      data: props.data,


      // infoWindowPosition: {
      //   longitude: 120,
      //   latitude: 30,
      // },
      infoWindowVisible: false,
      infoWindowData: {
        id: "",
        desc: "",
      },

      infoWindow: {
        position: {
          longitude: 120,
          latitude: 30,
        },
        visible: false,
        data: null,
      },
      useCluster:false
    }
    this.markerEvents = {
      created: (e) => {},
      mouseover: (e) => {
        const position = e.target.getPosition();
        this.setState({
          infoWindow: {
            position: {
              longitude: position.lng,
              latitude: position.lat,
            },
            visible: true,
            data: e.target.getExtData(),
          },
        });
      },
      mouseout: (e) => {
        this.setState({
          infoWindow: {
            position: {
              longitude: 120,
              latitude: 30,
            },
            visible: false,
            data: null,
          },
        });
      },
    };
  }
  
  componentWillUnmount() {
    if (this.map) {
      this.map.destroy();
    }
  }

  componentWillUnmount() {
    if (this.map) {
      this.map.destroy()
    }
  }
  
  // 缩放事件处理函数
  handleZoom = () => {
    const zoomLevel = this.map.getZoom();
    console.log('zoomLevel',zoomLevel)
    if (zoomLevel > 8) {
      this.setState({ useCluster: false });
    } else {
      this.setState({ useCluster: true });
    }
  };

  setFitViewWithZoneId(zoneId) {
    const fitOverlays = []
    this.map.getAllOverlays().forEach(polygon => {
      if (polygon.getExtData().zoneId === zoneId) {
        fitOverlays.push(polygon)
      }
    })

    if (fitOverlays.length) {
      this.map.setFitView(fitOverlays, false, undefined, this.map.getZoom())
    }
  }

  renderPolygons() {
    const { mainZoneId, polygons, toggleAdjacentZone, adjacentZoneIds } = this.props

    const l = polygons.length

    const _polygons = []

    for (let i = 0; i < l; i++) {
      const detail = polygons[i]
      if (detail.geoArea && detail.geoArea.length) {
        let _polygonType = polygonType.normal
        if (detail.zoneId === mainZoneId) {
          _polygonType = polygonType.main
        } else if (adjacentZoneIds.includes(detail.zoneId)) {
          _polygonType = polygonType.adjacent
        }

        detail.geoArea.forEach((path, pathId) => {
          _polygons.push(
            <Polygon 
              path={path}
              key={`${detail.id}:${pathId}`}
              style={getPolygonStyle(_polygonType)}
              events={{
                click: () => {
                  if (detail.zoneId === mainZoneId) {
                    return
                  }
                  toggleAdjacentZone(detail.zoneId)
                },
                mousemove: (e) => {
                  this.setState(() => ({
                    infoWindowPosition: e.lnglat,
                    infoWindowTitle: detail.zoneDesc
                  }))
                },
                mouseout: () => {
                  this.setState(() => ({
                    infoWindowPosition: null
                  }))
                }
              }}
              extData={{ zoneId: detail.zoneId }}
            />
          )
        })
      }
    }

    return _polygons
  }

  renderInfoWindow() {
    const { infoWindowPosition, infoWindowTitle } = this.state
    if (!infoWindowPosition) {
      return null
    }

    return <InfoWindow
      position={{
        longitude: infoWindowPosition.lng,
        latitude: infoWindowPosition.lat,
      }}
      isCustom={true}
      content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
      offset={[2,-10]}
      visible
    />
  }
  getPoint = ()=>{
    fetch(API_FETCH_ZONES_DEPT_POINT, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ deptId:this.props.deptId })
    }).then((response) => {
      if (response.ok) {
        return response.json();
      }
      throw new Error("Bad response from server");
    })
    .then((json) => {
      if(json.data instanceof Array){
        if (!json.data.length) {
          message.info("没有可显示的运输地点");
        }else{
          if(json.data.length > 500){
            this.setState({
              useCluster: true,
            })
          }
          json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
          this.setState({pointReferData:json.data},()=>{
           if (!this.map) return; // 如果地图实例未初始化,则不执行
            this.map.setFitView();
          })
        }
      }
    })
    .catch(e=>{})
  }
  renderInfo = () => {
    const { position, visible, data } = this.state.infoWindow;
    const {locTypes} = this.state
    if (!data) {
      return null;
    }
    const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
    const tds = (
      <div  className={styles.info}>
        <div className='inforow'>
          <span>运输地点代码</span>
          <span>{data.locId}</span>
        </div>
        <div className='inforow'>
          <span>运输地点描述</span>
          <span>{data.locDesc}</span>
        </div>
        <div className='inforow'>
          <span>类型</span>
          <span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
        </div>
        <div className='inforow'>
          <span>城市</span>
          <span>{data.city}</span>
        </div>
        <div className='inforow'>
          <span>省份</span>
          <span>{data.province}</span>
        </div>
        <div className='inforow'>
          <span>国家</span>
          <span>{data.country}</span>
        </div>
        <div className='inforow'>
          <span>经度</span>
          <span>{data.longitude}</span>
        </div>
        <div className='inforow'>
          <span>纬度</span>
          <span>{data.latitude}</span>
        </div>
        <div className='inforow'>
          <span>地址</span>
          <span>{data.addr}</span>
        </div>
        <div className='inforow'>
          <span>限行区域</span>
         <span>{data.udzDesc1}({data.udz1})</span>
        </div>
        <div className='inforow'>
          <span>业务区域</span>
         <span>{data.udzDesc2}({data.udz2})</span>
        </div>
      </div>
    );
    return (
      <InfoWindow
        position={position}
        visible={visible}
        isCustom={true}
        offset={[2, 2]}
      >
        {tds}
      </InfoWindow>
    );
  };
  pointRender = (extData) => {
    return (
      <div
        style={{
          color: "#4e72b8",
          width: "8px",
          height: "8px",
          borderRadius: "50%",
          background: "#4169E1",
          textAlign: "center",
        }}
      />
    );
  };
  initPointReferData = () => {
    if (!this.state.pointReferShow) {
      this.setState({
        pointReferData: [],
      });
    } else {
      this.getPoint()
    }
  };
  componentDidMount() {
    this.initPointReferData();
  }
  // 运输点参考
  // changePointReferShow = (checked) => {
  //   this.setState({ pointReferShow: checked }, () => {
  //     this.initPointReferData();
  //   });
  // };
  changePointReferShow = debounce((checked) => {
    this.setState({ pointReferShow: checked }, () => {
      this.initPointReferData();
    });
  }, 300); // 300ms 的防抖延迟
  render() {
    const {
      polygonActive,
      pointReferShow,
      data,
      pointReferData,
      areaReferData,
      areaReferShow,
    } = this.state;
    const option = {
      amapkey: MAP_AMAP_KEY,
      version: MAP_AMAP_VERSION,
      mapStyle: 'amap://styles/whitesmoke',
      // loading: this.renderLoading(),
      status: {
        resizeEnable: true,
      },
      plugins: ['ToolBar', 'Scale'],
      events: this.mapEvents,
    }

    return (
      <div style={{ width: "100%", height: "100vh", position: "relative" }}>
        <ActionsForm>
            <Form layout="inline">
              <Form.Item>
                <Switch
                  checkedChildren="显示地点"
                  unCheckedChildren="显示地点"
                  checked={pointReferShow}
                  onChange={this.changePointReferShow}
                />
              </Form.Item>
            </Form>
        </ActionsForm>
        <Map {...option}>
          <Markers
          markers={pointReferData}
          offset={[0,0]}
          render={this.pointRender}
          events={this.markerEvents}
          useCluster={this.state.useCluster}
        />
        {this.renderPolygons()}
        {this.renderInfoWindow()}
        {this.renderInfo()}
    </Map>
      </div>
    );
  }
}

希望点的数量少于1000时直接展示所有点,不聚合

import React, { Component } from "react";
import PropTypes from "prop-types";
import Immutable from 'seamless-immutable';
import {
  Map,
  Polygon,
  Markers,
  PolyEditor,
  MouseTool,
  InfoWindow,
} from "react-amap";
import {
  Form,
  Button,
  Switch,
  Tooltip,
  Modal,
  message,
  Menu,
  Dropdown,
  Popconfirm,
  Icon,
} from "antd";
import isomorphicFetch from "isomorphic-fetch";
import {
  API_FETCH_DOMAIN,
  API_FETCH_ZONES_DEPT_POINT,
  API_FETCH_ZONES_GROUP
} from 'constants/api'
import fetch from 'common/fetch'

import {
  MAP_AMAP_KEY,
  MAP_AMAP_DRIVING_KEY,
  MAP_AMAP_VERSION,
} from "constants/config";
import ActionsForm from "components/RoutePanel/ActionsForm";
import ZoneCascader from "components/PcaCascader/ZoneCascader";
import pstyles from "./polygons.scss";


// 获取封闭polygon(首尾点坐标一致)
const _getClosedPolygon = (paths) => {
  if (Array.isArray(paths) && paths.length > 2) {
    const firstLocation = paths[0];
    const lastLocation = paths[paths.length - 1];
    if (
      firstLocation[0] === lastLocation[0] &&
      firstLocation[1] === lastLocation[1]
    ) {
      return paths;
    } else {
      return [...paths, firstLocation];
    }
  } else {
    return [];
  }
};

class PolygonEdit extends Component {
  constructor(props) {
    super(props);
    this.state = {
      polygonActive: false,
      pointReferShow: true, //运输点参考
      pointReferData: [],
      areaReferShow: false, //已绘制参考
      areaReferData: [],
      locTypes: [],
      data: props.data,
      mapEditable: props.mapEditable,

      deleteMenuVisible: false,
      hoverItemData: [],

      adcode: "",
      province: "",
      provinceCode:"",
      city: "",
      cityCode:"",
      area: "",
      street: "",
      polyline: "",

      infoWindowPosition: {
        longitude: 120,
        latitude: 30,
      },
      infoWindowVisible: false,
      infoWindowData: {
        id: "",
        desc: "",
      },

      infoWindow: {
        position: {
          longitude: 120,
          latitude: 30,
        },
        visible: false,
        data: null,
      },
      showLabels: true, // 默认显示悬浮名称
      useCluster:false
    };

    const _this = this;
    this.amap = null; // 地图
    this.mouseTool = null; // 鼠标工具
    this.amapEvents = {
      created: (mapInstance) => {
        _this.amap = mapInstance;
        _this.amap.on('zoomend', this.handleZoom);
      },
      close: () => {
        _this.amap = null;
        if (_this.amap) {
          _this.amap.off('zoomend', this.handleZoom);
        }
      },
    };
    this.polyEditorEvents = {
      created: (ins) => {
        _this.amap.setFitView();
      },
      addnode: () => {},
      adjust: ({ lnglat, pixel, type, target } = option) => {},
      removenode: () => {},
      end: ({ type, target }) => {
        this.polyEditorEnd(target, type);
      },
    };
    this.polygonEvents = {
      created: (ins) => {
        _this.amap.setFitView();
      },
    };
    this.toolEvents = {
      created: (tool) => {
        _this.mouseTool = tool;
      },
      draw({ obj }) {
        _this.drawWhat(obj);
      },
    };
    this.markerEvents = {
      created: (e) => {},
      mouseover: (e) => {
        const position = e.target.getPosition();
        this.setState({
          infoWindow: {
            position: {
              longitude: position.lng,
              latitude: position.lat,
            },
            visible: true,
            data: e.target.getExtData(),
          },
        });
      },
      mouseout: (e) => {
        this.setState({
          infoWindow: {
            position: {
              longitude: 120,
              latitude: 30,
            },
            visible: false,
            data: null,
          },
        });
      },
    };
  }

  componentWillUnmount() {
    if (this.amap) {
      this.amap.destroy();
    }
  }

  componentDidMount() {
    if(this.props.isGroup){
      this.initPointReferData();
      this.initAreaReferData()
    }
    this.getLocType()
  }
   // 缩放事件处理函数
  handleZoom = () => {
    const zoomLevel = this.amap.getZoom();
    console.log('zoomLevel', zoomLevel);
    console.log('this.state.pointReferData.length', this.state.pointReferData.length);
  
    // 判断点的数量是否小于1000,以及当前缩放等级
    if (this.state.pointReferData.length > 1000 && zoomLevel < 10) {
      this.setState({ useCluster: true });
    } else {
      this.setState({ useCluster: false });
    }
  };
  getPoint = ()=>{
    fetch(API_FETCH_ZONES_DEPT_POINT, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ deptId:this.props.deptId })
    }).then((response) => {
      if (response.ok) {
        return response.json();
      }
      throw new Error("Bad response from server");
    })
    .then((json) => {
      if(json.data instanceof Array){
        if (!json.data.length) {
          message.info("没有可显示的运输地点");
        }else{
          json.data.forEach(d=>d.position= { longitude: d.longitude, latitude: d.latitude })
          this.setState({pointReferData:json.data},()=>{
            if (!this.amap) return; // 如果地图实例未初始化,则不执行
            this.amap.setFitView();
          })
        }
      }
    })
    .catch(e=>{})
  }
  renderPolygons = () => {
    const { areaReferShow, areaReferData } = this.state;

    const GROUP_KEY = {
      unConfiguredGroups: 'unConfiguredGroups',
      configuredGroups: 'configuredGroups'
    }
    const prepareGroups = (groups) => {
      const _groups = Immutable
        .asMutable(groups, { deep: true })
        .map(group => ({
          ...group,
          zoneDetail: (group.zoneDetail || []).map(detail => {
            return {
              ...detail,
              geoArea: JSON.parse(detail.geoArea)
            }
          })
        }))
      return {
        groups,
        [GROUP_KEY.unConfiguredGroups]: _groups
          .filter(group => !group.lines.length)
          .map(group => ({
            ...group,
            lines: []
          })),
        [GROUP_KEY.configuredGroups]: _groups.filter(group => group.lines.length),
        zoneOptions: _groups.map(group => ({
          value: group.zoneId,
          label: group.zoneDesc,
        })),
        polygons: _groups.reduce((polygons, group) => {
          polygons.push(...group.zoneDetail.map(zoneDetail => ({ ...zoneDetail, zoneDesc: group.zoneDesc }))) // flat zoneDetail
     
          return polygons
        }, [])
      }
    }

    const newData = prepareGroups(areaReferData)
    const polygons = newData.polygons
    const l = polygons.length
    const _polygons = []
    for (let i = 0; i < l; i++) {
      const detail = polygons[i]
      if (detail.geoArea && Array.isArray(detail.geoArea) && detail.geoArea.length) {
        detail.geoArea.forEach((path, pathId) => {
          _polygons.push(
            <Polygon 
              path={path}
              key={`${detail.id}:${pathId}`}
              style={{
                fillColor: '#333333',
                fillOpacity: 0.3,
                strokeOpacity: 0.5,
                strokeWeight: 1,
                strokeColor: '#333333',
              }}
              events={{
                mousemove: (e) => {
                  this.setState(() => ({
                    infoWindowPosition: e.lnglat,
                    infoWindowTitle: detail.zoneDesc
                  }))
                },
                mouseout: () => {
                  this.setState(() => ({
                    infoWindowPosition: null
                  }))
                }
              }}
              extData={{ zoneId: detail.zoneId }}
            />
          )
        })
      }else{
        console.log('detail.geoArea',detail.geoArea)
      }
    }
  return _polygons
  
  }
  getArea = ()=>{
    fetch(API_FETCH_ZONES_GROUP, {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ branchId:this.props.branchId,deptId:this.props.deptId})
    }).then((response) => {
      if (response.ok) {
        return response.json();
      }
      throw new Error("Bad response from server");
    })
    .then((json) => {
      if(json.data && json.data.data instanceof Array){
        if (!json.data.data.length) {
          message.info("没有可显示的区域");
        }else{

          this.setState({areaReferData:json.data.data})

        }
      }
    })
    .catch(e=>{
      console.log('e',e)
    })
  }
  renderInfoWindow() {
    const { infoWindowPosition, infoWindowTitle, showLabels } = this.state
    if(showLabels){
      if (!infoWindowPosition) {
        return null
      }

      return <InfoWindow
        position={{
          longitude: infoWindowPosition.lng,
          latitude: infoWindowPosition.lat,
        }}
        isCustom={true}
        content={`<div style="pointer-events: none;background: #fff;border:1px solid silver;padding: 4px 8px;">${infoWindowTitle}</div>`}
        offset={[2,2]}
        visible
      />
    }
  }
  initPointReferData = () => {
    if (!this.state.pointReferShow) {
      this.setState({
        pointReferData: [],
      });
    } else {
      this.getPoint()
    }
  };
  initAreaReferData = () => {
    if (!this.state.areaReferShow) {
      this.setState({
        areaReferData: [],
      });
    } else {
      this.getArea()
    }
  };

  renderLoading = () => {
    const loadingStyle = {
      position: "relative",
      height: "100%",
      width: "100%",
      display: "flex",
      justifyContent: "center",
      alignItems: "center",
    };
    return <div style={loadingStyle}>Loading Map...</div>;
  };

  // 运输点参考
  changePointReferShow = (checked) => {
    this.setState({ pointReferShow: checked }, () => {
      this.initPointReferData();
    });
  };

  // 已绘制区域参考
  changeAreaReferShow = (checked) => {
    this.setState({ areaReferShow: checked }, () => {
      this.initAreaReferData();
    });
  };
   // 是否悬浮显示名称
  toggleLabels = (checked) => {
    this.setState({ showLabels: checked });
  };

  pointRender = (extData) => {
    return (
      <div
        style={{
          color: "#4e72b8",
          width: "8px",
          height: "8px",
          borderRadius: "50%",
          background: "#4169E1",
          textAlign: "center",
        }}
      />
    );
  };

  getLocType=()=>{
    fetch(`${API_FETCH_DOMAIN}LOCATION_TYPE`,{
      credentials: 'include'
    })
    .then((response) => {
      if (response.ok) {
        return response.json();
      }
      throw new Error("Bad response from server");
    })
    .then((json) => {
      if(json.data instanceof Array){
        this.setState({locTypes:json.data})
      }
    })
    .catch(e=>{})
  }
  renderInfo = () => {
    const { position, visible, data } = this.state.infoWindow;
    const {locTypes} = this.state
    if (!data) {
      return null;
    }
    const locTypeItem = locTypes.find(t=>t.domVal==data.locType)
    const tds = (
      <div  className={pstyles.pinfo}>
        <div className='inforow'>
          <span>运输地点代码</span>
          <span>{data.locId}</span>
        </div>
        <div className='inforow'>
          <span>运输地点描述</span>
          <span>{data.locDesc}</span>
        </div>
        <div className='inforow'>
          <span>类型</span>
          <span>{locTypeItem?locTypeItem.domValDesc:data.locType}</span>
        </div>
        <div className='inforow'>
          <span>城市</span>
          <span>{data.city}</span>
        </div>
        <div className='inforow'>
          <span>省份</span>
          <span>{data.province}</span>
        </div>
        <div className='inforow'>
          <span>国家</span>
          <span>{data.country}</span>
        </div>
        <div className='inforow'>
          <span>经度</span>
          <span>{data.longitude}</span>
        </div>
        <div className='inforow'>
          <span>纬度</span>
          <span>{data.latitude}</span>
        </div>
        <div className='inforow'>
          <span>地址</span>
          <span>{data.addr}</span>
        </div>
        <div className='inforow'>
          <span>限行区域</span>
         <span>{data.udzDesc1}({data.udz1})</span>
        </div>
        <div className='inforow'>
          <span>业务区域</span>
         <span>{data.udzDesc2}({data.udz2})</span>
        </div>
      </div>
    );
    return (
      <InfoWindow
        position={position}
        visible={visible}
        isCustom={true}
        offset={[2, 2]}
      >
        {tds}
      </InfoWindow>
    );
  };
  // polygon edit switch
  togglePolygon = (checked, e) => {
    if (checked) {
      // open edit and close add if it is drawing
      if (this.mouseTool) {
        this.mouseTool.close();
      }
      this.setState({ polygonActive: true, isDrawingPolygon: false });
    } else {
      // close edit
      this.setState({ polygonActive: false });
    }
  };

  // polygon add switch
  toggleDrawPolygon = (checked, e) => {
    if (checked) {
      if (this.mouseTool) {
        this.mouseTool.polygon();
        this.setState({ isDrawingPolygon: true });
        message.success("鼠标左键双击或右键单击完成当前多边形!");
      }
    } else {
      if (this.mouseTool) {
        this.mouseTool.close();
        this.setState({ isDrawingPolygon: false });
      }
    }
  };

  // finish polygon draw
  drawWhat = (obj) => {
    const paths = obj.getPath();
    let data = this.state.data.slice();
    const pathData = paths.map((item) => [item.lng, item.lat]);
    if (pathData.length > 2) {
      if (this.amap) {
        this.amap.remove(obj);
      }
      data.push(_getClosedPolygon(pathData));
      this.setState({ data });
      message.success(
        `您成功绘制了一个${
          paths.length
        }边形,可继续绘制或点击结束多边形绘制按钮`
      );
    }
  };

  // polygon editor end
  polyEditorEnd = (target, type) => {
    const paths = target.getPath();
    let isSinglePath = false; // 是否是单围栏
    const pathData = paths.map((item, index) => {
      if (Array.isArray(item)) {
        const itemPaths = item.map((element) => [element.lng, element.lat]);
        return _getClosedPolygon(itemPaths);
      } else {
        isSinglePath = true;
        return [item.lng, item.lat];
      }
    });
    this.setState({
      data: isSinglePath ? [_getClosedPolygon(pathData)] : pathData,
    });
  };

  // 多边形删除下拉菜单
  deleteMenuVisibleChange = (flag) => {
    this.setState({ deleteMenuVisible: flag });
  };
  // 删除多边形
  handleMenuClick = (e) => {
    this.handleDeletePath(e.key);
  };
  handleDeletePath = (key) => {
    const path = [...this.state.data];
    path.splice(key, 1);
    this.setState({ data: path, deleteMenuVisible: false });
  };
  handleMenuHover = (key) => {
    this.setState({ hoverItemData: this.state.data[key] });
  };

  handleChangeAdcode = ({ adcode, province, city, area, street,provinceCode,cityCode }) => {
    this.setState({ adcode, province, city, area, street,provinceCode, cityCode});
  };

  handleAdcodeCancle = () => {
    this.setState({
      adcode: "",
      province: "",
      provinceCode:"",
      city: "",
      cityCode:"",
      area: "",
      street: "",
    });
  };

  handleAdcodeConfirm = () => {
    const { adcode, provinceCode, cityCode, area, street, mapEditable } = this.state;
    if (adcode) {
      this.setState({
        mapEditable: "N",
        adcode: "",
        polygonActive: false,
        isDrawingPolygon: false,
      });
      if (street) {
        isomorphicFetch(`/city-tiles/${provinceCode}_${cityCode}.json`)
          .then((response) => {
            if (response.ok) {
              return response.json();
            }
            throw new Error("Bad response from server");
          })
          .then((json) => {
            if (json.status === "0") {
              throw new Error("No districts from server");
            }
            try {
              const streetItemData = json.features.find(
                (s) => s.properties.subdistrict == street
              ).geometry.coordinates;
              if (this.mouseTool) {
                this.mouseTool.close();
              }
              this.setState({
                data: streetItemData, // some data
              });
              if (this.amap) {
                this.amap.setFitView();
              }
            } catch (e) {
              message.error("获取行政区划数据失败!");
            }
            // const polyline = (json.districts && json.districts[0]) ? json.districts[0].polyline : [];
            // const data = polyline.split('|').map(block => block.split(';').map(pointer => pointer.split(',').map(lnglat => Number(lnglat))));
            // if (this.mouseTool){
            //   this.mouseTool.close();
            // }
            // this.setState({
            //   data: data, // some data
            // })
            // if (this.amap) {
            //   this.amap.setFitView();
            // }
          })
          .catch((error) => {
            message.error("获取行政区划数据失败!");
            this.setState({
              mapEditable: "Y",
              province: "",
              provinceCode:"",
              city: "",
              cityCode:"",
              area: "",
              street: "",
              data: [],
            });
          });
      } else {
        // fetch polyline data
        isomorphicFetch(
          `//restapi.amap.com/v3/config/district?key=e17fafe279209e4b3a303cc907347277&keywords=${adcode}&subdistrict=0&extensions=all`
        )
          .then((response) => {
            if (response.ok) {
              return response.json();
            }
            throw new Error("Bad response from server");
          })
          .then((json) => {
            if (json.status === "0") {
              throw new Error("No districts from server");
            }
            const polyline =
              json.districts && json.districts[0]
                ? json.districts[0].polyline
                : [];
            const data = polyline
              .split("|")
              .map((block) =>
                block
                  .split(";")
                  .map((pointer) =>
                    pointer.split(",").map((lnglat) => Number(lnglat))
                  )
              );
            if (this.mouseTool) {
              this.mouseTool.close();
            }
            this.setState({
              data: data, // some data
            });
            if (this.amap) {
              this.amap.setFitView();
            }
          })
          .catch((error) => {
            message.error("获取行政区划数据失败!");
            this.setState({
              mapEditable: "Y",
              province: "",
              provinceCode:"",
              city: "",
              cityCode:"",
              area: "",
              street: "",
              data: [],
            });
          });
      }
    } else {
      if (mapEditable === "N") {
        this.setState({
          mapEditable: "Y",
          adcode: "",
          province: "",
          provinceCode:"",
          city: "",
          cityCode:"",
          area: "",
          street: "",
          data: [],
        });
      }
    }
  };

  handleFinished = () => {
    const {
      polygonActive,
      data,
      adcode,
      province,
      city,
      area,
      street,
      mapEditable,
    } = this.state;
    let result = {
      geoArea:
        Array.isArray(data) && data.length > 0 ? JSON.stringify(data) : null,
      mapEditable,
    };
    if (mapEditable === "N") {
      Object.assign(result, {
        country: "中国",
        state: province,
        city,
        district: area,
        subdistrict: street,
      });
    }
    if (polygonActive) {
      this.setState({ polygonActive: false });
      Modal.confirm({
        title: "您的多边形调整还未结束,是否结束?",
        onOk: () => {
          this.props.onConfirm(result);
        },
        onCancel: () => {
          this.setState({ polygonActive: true });
        },
      });
    } else {
      this.props.onConfirm(result);
    }
  };

  render() {
    const {
      polygonActive,
      isDrawingPolygon,
      pointReferShow,
      data,
      pointReferData,
      areaReferData,
      areaReferShow,
      deleteMenuVisible,
      hoverItemData,
      adcode,
      mapEditable,
      infoWindowPosition,
      infoWindowVisible,
      infoWindowData,
      showLabels,
      useCluster
    } = this.state;
    const { onCancle, isGroup } = this.props;
    const option = {
      amapkey: MAP_AMAP_KEY,
      version: MAP_AMAP_VERSION,
      mapStyle: "amap://styles/whitesmoke",
      loading: this.renderLoading(),
      status: {
        resizeEnable: true,
      },
      plugins: ["ToolBar", "Scale"],
      events: this.amapEvents,
    };
    return (
      <div style={{ width: "100%", height: "100vh", position: "relative" }}>
        <ActionsForm>
          {isGroup ? (
            <Form layout="inline">
              <Form.Item>
                <Popconfirm
                  title={
                    <ZoneCascader
                      adcode={adcode}
                      onChange={this.handleChangeAdcode}
                    />
                  }
                  icon={
                    <Tooltip title="选择行政区划。此操作将首先清空已有围栏,已选择的行政区划围栏不允许编辑">
                      <Icon type="question-circle-o" />
                    </Tooltip>
                  }
                  onCancel={this.handleAdcodeCancle}
                  onConfirm={this.handleAdcodeConfirm}
                >
                  <Button type="primary">行政区划选择{isGroup}</Button>
                </Popconfirm>
              </Form.Item>
              <Form.Item>
                <Switch
                  disabled={mapEditable === "N"}
                  checkedChildren="调整"
                  unCheckedChildren="调整"
                  checked={polygonActive}
                  onChange={this.togglePolygon}
                />
              </Form.Item>
              <Form.Item>
                <Switch
                  disabled={mapEditable === "N"}
                  checkedChildren="新增"
                  unCheckedChildren="新增"
                  checked={isDrawingPolygon}
                  onChange={this.toggleDrawPolygon}
                />
              </Form.Item>
              <Form.Item>
                <Dropdown
                  overlay={
                    <Menu onClick={this.handleMenuClick}>
                      {data &&
                        data.length > 0 &&
                        data.map((item, index) => (
                          <Menu.Item key={index}>
                            <span
                              onMouseOver={() => this.handleMenuHover(index)}
                            >{`${index + 1} 删除`}</span>
                          </Menu.Item>
                        ))}
                    </Menu>
                  }
                  onVisibleChange={this.deleteMenuVisibleChange}
                  visible={deleteMenuVisible}
                  disabled={mapEditable === "N"}
                >
                  <Button>删除</Button>
                </Dropdown>
              </Form.Item>
              <Form.Item>
                <Button onClick={onCancle}>取消</Button>
              </Form.Item>
              <Form.Item>
                <Button type="primary" onClick={this.handleFinished}>
                  完成
                </Button>
              </Form.Item>
              <Form.Item>
                <Switch
                  checkedChildren="显示地点"
                  unCheckedChildren="显示地点"
                  checked={pointReferShow}
                  onChange={this.changePointReferShow}
                />
              </Form.Item>
              <Form.Item>
                <Switch
                  checkedChildren="显示其他区域"
                  unCheckedChildren="显示其他区域"
                  checked={areaReferShow}
                  onChange={this.changeAreaReferShow}
                />
              </Form.Item>
              <Form.Item>
                <Switch
                  checkedChildren="显示区域名称"
                  unCheckedChildren="隐藏区域名称"
                  checked={showLabels}
                  onChange={this.toggleLabels}
                />
              </Form.Item>
            </Form>
          ) : (
            <Form layout="inline">
              <Form.Item>
                <Button type="primary" onClick={onCancle}>
                  关闭
                </Button>
              </Form.Item>
            </Form>
          )}
        </ActionsForm>
        <Map {...option}>
          <Markers
            markers={pointReferData}
            offset={[0,0]}
            render={this.pointRender}
            events={this.markerEvents}
            useCluster={useCluster}
          />
          {isGroup && <MouseTool events={this.toolEvents} />}
          {isGroup ? (
            <Polygon
              path={data}
              style={{ fillOpacity: 0.3, strokeOpacity: 0.5, strokeWeight: 1 }}
              events={this.polygonEvents}
            >
              <PolyEditor
                active={
                  polygonActive && data.length > 0 && data.flat().length <= 100
                }
                events={this.polyEditorEvents}
              />
            </Polygon>
          ) : (
            data.map((item, index) => (
              <Polygon
                events={this.polygonEvents}
                key={index}
                path={item}
                style={{
                  fillOpacity: 0.3,
                  strokeOpacity: 0.5,
                  strokeWeight: 1,
                }}
              />
            ))
          )}
          <Polygon
            visible={deleteMenuVisible}
            path={[hoverItemData]}
            zIndex={100}
            style={{
              fillColor: "red",
              fillOpacity: 0.3,
              strokeOpacity: 0.5,
              strokeWeight: 1,
            }}
          />
          {this.renderInfo()}
          {this.renderPolygons()}
          {this.renderInfoWindow()}
        </Map>
      </div>
    );
  }
}

PolygonEdit.propTypes = {
  isGroup: PropTypes.bool, // 是:data 为单个 polygon 数据;否: data 为多个 polygon 数据
  data: PropTypes.array,
  onCancle: PropTypes.func,
  onConfirm: PropTypes.func,
};
PolygonEdit.defaultProps = {
  isGroup: true,
  data: [],
  onCancle: () => {},
  onConfirm: () => {},
};

export default PolygonEdit;


http://www.kler.cn/a/407947.html

相关文章:

  • uniapp 自定义popup 弹窗 简单封装(微信小程序)
  • 从 HTML 到 CSS:开启网页样式之旅(二)—— 深入探索 CSS 选择器的奥秘
  • Python的3D可视化库 - vedo (2)visual子模块 基本可视化行为
  • react中useMemo的使用场景
  • (附项目源码)Java开发语言,219 ssm律师事务所业务管理系统的设计与实现,计算机毕设程序开发+文案(LW+PPT)
  • Docker核心概念总结
  • python 正则表达式re 模块的基本使用方法
  • Spark 中 RDD checkpoint 是通过启动两个独立的 Job 完成的。
  • Spring Boot驱动的高效OA解决方案
  • nc网络工具的使用
  • 《Python编程实训快速上手》第八天--组织文件
  • 订单日记为“惠采科技”提供全方位的进销存管理支持
  • linux安装cyberRT6.0
  • 【Linux驱动开发】驱动中的信号 异步通知开发
  • Kotlin:后端开发的新宠
  • 面试经典 150 题:205,55
  • 【Linux 报错】SSH服务器拒绝了密码。请再试一次
  • 【Spring编程常见错误50例】04. Spring Bean 生命周期常见错误-上
  • 软件工程导论 选填题知识点总结
  • ArcGIS Pro 3.4新功能1:唯一值符号化增加复选框,可在内容窗格和布局视图中控制类别的可见性。
  • 实现一个string的indexof方法,给出时空复杂度估计
  • HarmonyOS Next原创项目
  • 乐理的学习(调式)
  • 通过socket设置版本更新提示
  • 鸿蒙HarmonyOS学习笔记(1)
  • 工程师 - 智能家居方案介绍