UNPKG

@mapgis/webclient-leaflet-plugin

Version:

2,212 lines 74 kB

<!DOCTYPE html>
<html lang="en">

<head>
  
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title> view/MapView.js</title>
  <!--[if lt IE 9]>
    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
  <link type="text/css" rel="stylesheet" href="styles/app.min.css">
  <link type="text/css" rel="stylesheet" href="styles/iframe.css">
  <link type="text/css" rel="stylesheet" href="">

  
</head>



<body class="layout small-header">
    <div id="stickyNavbarOverlay"></div>
    

<div class="top-nav">
    <div class="inner">
        <a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
        </a>
        <div class="logo">
            
                <h1> MapGIS Client for JavaScript API</h1>
            
            
        </div>
        <div class="menu">
            
            <div class="navigation">
                
                    
                        <a
                            class="link user-link "
                            href="/docs/cesium/index.html"
                        >
                            Cesium
                        </a>
                    
                        <a
                            class="link user-link "
                            href="/docs/mapboxgl/index.html"
                        >
                            MapboxGL
                        </a>
                    
                        <a
                            class="link user-link "
                            href="/docs/leaflet/index.html"
                        >
                            Leaflet
                        </a>
                    
                        <a
                            class="link user-link "
                            href="/docs/openlayers/index.html"
                        >
                            OpenLayers
                        </a>
                    
                

            </div>
        </div>
    </div>
</div>

    <div id="main">
        <div
            class="sidebar "
            id="sidebarNav"
        >
            <div>
                <span class="mapgis-api-document-span"><a href="index.html">API文档</a></span>
            </div>
            
                <div class="search-wrapper">
                    <input id="search" type="text" placeholder="搜索文档..." class="input">
                </div>
            
            <nav>
                
                    <div class="category"><div style="font-weight: bold;vertical-align: middle;padding: 0.4rem 0;" class="mapgis-menu-span"><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/m.png" alt="">视图模块</div><ul class="mapgis-sidebar-menus"><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="DrawControl.html">DrawControl</a><span style="display: none;"><p>绘制工具</p></span></li><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="MapView.html">MapView</a><span style="display: none;"><p>二维场景视图(leaflet引擎),对地图引擎进行管理,如果要对地图图层进行管理请参考[Map]{@link Map},<br/>
参考示例:
<a href='#MapView'>[初始化二维场景视图]</a>
<br>[ES5引入方式]:<br/>
Zondy.MapView() <br/>
[ES6引入方式]:<br/>
import { MapView } from '@mapgis/webclient-leaflet-plugin' <br/></p></span></li><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="Popup.html">Popup</a><span style="display: none;"><p>二维场景信息弹窗(leaflet引擎)
参考示例:
<a href='#MapView'>[初始化二维场景视图]</a>
[ES6引入方式]:<br/>
import { Popup } from '@mapgis/webclient-leaflet-plugin' <br/>
自定义样式说明:<br/>
zondy-popup__content 弹窗容器样式<br/>
zondy-popup__tip 弹窗对话框箭头样式<br/>
zondy-popup__header 弹窗头部样式<br/>
zondy-popup__content 弹窗主体样式<br/>
zondy-popup__footer 弹窗底部样式<br/></p></span></li><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="Screenshot.html">Screenshot</a><span style="display: none;"><p>屏幕打印工具</p></span></li></ul><div style="font-weight: bold;vertical-align: middle;padding: 0.4rem 0;" class="mapgis-menu-span"><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/m.png" alt="">草图编辑模块</div><ul class="mapgis-sidebar-menus"><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="SketchEditorLeaflet.html">SketchEditorLeaflet</a><span style="display: none;"><p>二维场景草图编辑类<br/>
<br>[ES5引入方式]:<br/>
const { SketchEditorLeaflet } = Zondy <br/>
[ES6引入方式]:<br/>
import { SketchEditorLeaflet } from &quot;@mapgis/webclient-leaflet-plugin&quot; <br/></p></span></li></ul><div style="font-weight: bold;vertical-align: middle;padding: 0.4rem 0;" class="mapgis-menu-span"><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/m.png" alt="">渲染器模块</div><ul class="mapgis-sidebar-menus"><li><img style="width: 20px;margin-right: 6px;vertical-align: middle;" src="./styles/c.png" alt=""><a href="Zondy.ThemeLayer.GraphThemeLayer.html">GraphThemeLayer</a><span style="display: none;"><p>统计专题图通过为每个要素绘制统计图表来反映其对应的专题值的大小。它可同时表示多个字段属性信息,在区域本身与各区域之间形成横向和纵向的对比。<br>统计专题图多用于具有相关数量特征的地图上,比如表示不同地区多年的粮食产量、GDP、人口等,不同时段客运量、地铁流量等。目前提供的统计图类型有:柱状图(Bar),折线图(Line),饼图(Pie),三维柱状图(Bar3D),点状图(Point),环状图(Ring)。</p></span></li></ul></div>
                
            </nav>
        </div>
        <div class="core" id="main-content-wrapper">
            <div class="content">
                <header id="page-title" class="page-title">
                    <span class="page-title-main">类名</span>
                    <span class="page-title-sub">view/MapView.js</span>
                </header>
                



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>import {
  Zondy,
  defaultValue,
  BaseView,
  ViewEventType,
  LayerType,
  Point,
  Extent,
  T,
  showLayerByScale,
  SpatialReference,
  Projection,
  MapEventType,
  Geometry,
  Config,
  cloneObject,
  TileInfoUtil,
  // Utils,
  Map,
  isNull,
  toJSON
} from '@mapgis/webclient-common'
import * as L from '@mapgis/leaflet'
import UI from './ui/UI'
import MapViewUtil from './utils/MapViewUtil'
import { getLayerView } from '../document/index'
import CRS from './utils/support/CRS'
import Popup from './utils/Popup'
import Screenshot from './utils/Screenshot'

/**
 * 二维场景视图(leaflet引擎),对地图引擎进行管理,如果要对地图图层进行管理请参考[Map]{@link Map},&lt;br/>
 * 参考示例:
 * &lt;a href='#MapView'>[初始化二维场景视图]&lt;/a>
 * &lt;br>[ES5引入方式]:&lt;br/>
 * Zondy.MapView() &lt;br/>
 * [ES6引入方式]:&lt;br/>
 * import { MapView } from '@mapgis/webclient-leaflet-plugin' &lt;br/>
 * @class MapView
 * @extends Evented
 * @moduleEX ViewModule
 * @fires BaseView#地图视图加载完毕事件
 * @fires BaseView#鼠标点击事件
 * @fires BaseView#鼠标双击事件
 * @fires BaseView#鼠标按下事件
 * @fires BaseView#鼠标抬起事件
 * @fires BaseView#鼠标右键点击事件
 * @fires BaseView#鼠标移动事件
 * @fires BaseView#鼠标移出视图事件
 * @fires BaseView#鼠标移入视图事件
 * @fires BaseView#zoom变化事件
 * @fires BaseView#地图移动事件
 * @fires BaseView#地图大小变化事件
 * @fires BaseView#键盘输入事件
 * @fires BaseView#键盘按下事件
 * @fires BaseView#键盘抬起事件
 * @fires BaseView#地图视图改变事件
 * @param {Object} options 构造参数
 * @param {Map}  [options.Map]  图层管理容器对象
 * @param {String} [options.viewId] 二维场景视图的容器(html的div标签)ID
 * @param {Number} [options.minZoom = 0] 最小缩放级数,在此设置则所有图层都不会浏览小于该级数的图片
 * @param {Number} [options.maxZoom = 19] 最大缩放级数,在此设置则所有图层都不会浏览大于该级数的图片
 * @param {Number} [options.zoom = 1] 初始化二维场景视图时显示级数
 * @param {Boolean} [options.attributionControl = false] 是否显示右下角水印
 * @param {Boolean} [options.zoomControl = true] 是否显示缩放控件
 * @param {Boolean | String} [options.doubleClickZoom = true] 是否允许双击鼠标左键缩放或者缩放至图层中心点
 * @param {Boolean} [options.dragging = true] 是否允许拖拽
 * @param {Number} [options.zoomSnap = 1] 当使用flyTo缩放至中心点时,缩放级数乘以的系数
 * @param {Number} [options.zoomDelta = 1] 当触发zoomIn或者zoomOut操作时,缩放级数乘以的系数
 * @param {Boolean} [options.trackResize = true] 是否允许图层大小随视窗变化
 * @param {Boolean} [options.keyboard = true] 是否允使用键盘的+/-号,来缩放地图
 * @param {Number} [options.keyboardPanDelta = 80] 使用键盘来平移或缩放地图时的系数,单位px
 * @param {Boolean | String} [options.scrollWheelZoom = true] 使用键盘来平移或缩放地图时的系数,单位px
 * @param {Number} [options.wheelDebounceTime = 40] 滚轮事件的触发事件,单位毫秒
 * @param {Number} [options.wheelPxPerZoomLevel = 60] 滚轮缩放时,地图缩放的像素单位,单位像素
 * @param {Boolean} [options.tapHold = true] 是否开启移动端,手指按压不放事件
 * @param {Number} [options.tapTolerance = 15] 手指有效触发范围,单位像素
 * @param {Boolean | String} [options.touchZoom = true] 是否启用手指两指缩放,当值为center,表示两只滑动,缩放至地图中心
 * @param {Boolean} [options.bounceAtZoomLimits = true] 当过最大或最小级数后不再缩放
 * @param {Boolean} [options.animation = true] 是否启用动画
 * @param {Point} [options.center = new Point({coordinates:[0,0]})] 地图视图中心点
 * @param {Extent} [options.extent = undefined] 地图视图可视范围
 * @param {Number} [options.scale = undefined] 地图视图比例尺
 * @param {Number} [options.maxScale = undefined] 最大比例尺
 * @param {Number} [options.minScale = undefined] 最小比例尺
 * @param {Object} [options.popup = undefined] 地图弹框
 * @param {Number} [options.rotation = 0] 地图视图旋转选项。单位为度,默认为0,表示不进行旋转
 *
 * @summary &lt;h5>支持如下方法:&lt;/h5>
 * &lt;a href='#flyTo'>[1、视点跳转]&lt;/a>&lt;br/>
 * &lt;a href='#destroy'>[2、销毁视图对象]&lt;/a>&lt;br/>
 * &lt;a href='#getCenter'>[3、获取当前视图的中心点]&lt;/a>&lt;br/>
 * &lt;a href='#getPixelCenter'>[3、获取当前视图的像素中心点]&lt;/a>&lt;br/>
 * &lt;a href='#getZoom'>[4、获取当前缩放级数]&lt;/a>&lt;br/>
 * &lt;a href='#getExtent'>[5、获取当前视图的地理范围]&lt;/a>&lt;br/>
 * &lt;a href='#getPixelWorldExtent'>[6、获取当前视图的像素范围]&lt;/a>&lt;br/>
 * &lt;a href='#getMinZoom'>[7、获取最小缩放级数]&lt;/a>&lt;br/>
 * &lt;a href='#getMaxZoom'>[8、获取最大缩放级数]&lt;/a>&lt;br/>
 * &lt;a href='#getSize'>[9、获取当前视图容器的宽高]&lt;/a>&lt;br/>
 * &lt;a href='#toJSON'>[10、导出场景视图的配置文件]&lt;/a>&lt;br/>
 * &lt;a href='#clone'>[11、克隆并返回一个新的场景视图对象]&lt;/a>&lt;br/>
 * &lt;a href='#fromJSON'>[12、通过json构造并返回一个新的场景视图对象]&lt;/a>&lt;br/>
 * &lt;a href='#takeScreenshot'>[13、屏幕快照]&lt;/a>&lt;br/>
 * [14、注册事件]{@link Evented#on}&lt;br/>
 * [15、移除事件]{@link Evented#off}&lt;br/>
 * &lt;a href='#toMap'>[15、屏幕像素坐标点转地理坐标点]&lt;/a>&lt;br/>
 * &lt;a href='#toScreen'>[16、地理坐标点转屏幕像素坐标点]&lt;/a>&lt;br/>
 * &lt;a href='#hitTest'>[17、穿透检测]&lt;/a>&lt;br/>
 * &lt;a href='#getLayer'>[18、根据实际图层对象查询并返回基础图层]&lt;/a>&lt;br/>
 * &lt;a href='#getScale'>[19、获取当前比例尺]&lt;/a>&lt;br/>
 *
 * @example &lt;caption>&lt;h7 id='MapView'>初始化一个二维场景视图&lt;/h7>&lt;/caption>
 * // ES5引入方式
 * const { Map, MapView } = Zondy
 * // ES6引入方式
 * import { Map, MapView } from "@mapgis/webclient-leaflet-plugin"
 * // 初始化图层管理容器
 * const map = new Map();
 * // 初始化地图视图对象
 * const mapView = new MapView({
 *   // 二维场景视图的容器(html的div标签)ID
 *   viewId: "二维场景视图的容器的id",
 *   // 图层管理容器
 *   map: map
 * });
 */
class MapView extends BaseView {
  constructor(options) {
    super(options)
    // eslint-disable-next-line no-param-reassign
    options = defaultValue(options, {})
    this._options = options
    /**
     * 地图视图中心点
     * @member {Point} MapView.prototype.center
     */
    // 统一center的类型
    this._center = defaultValue(
      options.center,
      new Point({ coordinates: [0, 0] })
    )
    if (
      this._center &amp;&amp;
      Array.isArray(this._center) &amp;&amp;
      this._center.length > 1
    ) {
      this._center = new Point({
        coordinates: [this._center[0], this._center[1]]
      })
    }
    this.crs = undefined // defaultValue(options.crs, 'EPSG3857')
    /**
     * 地图视图可视范围
     * @member {Extent} MapView.prototype.extent
     */
    this._extent = defaultValue(options.extent, undefined)
    /**
     * 是否启用视角跳转动画
     * @member {Boolean} MapView.prototype.animation
     */
    this.animation = defaultValue(options.animation, true)
    /**
     * 地图视图宽度
     * @readonly
     * @member {Number} MapView.prototype.width
     */
    this.width = undefined
    /**
     * 地图视图高度
     * @readonly
     * @member {Number} MapView.prototype.height
     */
    this.height = undefined
    /**
     * 地图层级
     * @member {Number} MapView.prototype.zoom
     */
    this._zoom = defaultValue(options.zoom, 0)
    this._resolution = 0 // defaultValue(options.resolution, 0)
    /**
     * 地图视图比例尺
     * @member {Number} MapView.prototype.scale
     */
    this._scale = defaultValue(options.scale, undefined)
    /**
     * 地图视图最大比例尺
     * @member {Number} MapView.prototype.maxScale
     */
    this.maxScale = defaultValue(options.maxScale, undefined)
    /**
     * 地图视图最小比例尺
     * @member {Number} MapView.prototype.minScale
     */
    this.minScale = defaultValue(options.minScale, undefined)
    /**
     * 地图弹框popup
     * @member {Popup} MapView.prototype.popup
     */
    this.popup = defaultValue(options.popup, undefined)
    /**
     * 视图渲染方式是否为canvas
     * @member {Boolean} MapView.prototype.preferCanvas
     */
    this.preferCanvas = defaultValue(options.preferCanvas, false)
    // if (typeof this.crs === 'string') {
    //   if (L.CRS[this.crs]) {
    //     this.crs = L.CRS[this.crs]
    //   } else {
    //     this.crs = L.CRS.EPSG4326
    //   }
    // }
    /**
     * 视图空间参考系
     * @readonly
     * @member {SpatialReference} MapView.prototype._spatialReference
     */
    this._spatialReference = undefined
    this._referenceLayer = undefined
    /**
     * 是否锁定视图空间参考系
     * @member {Boolean} MapView.prototype.spatialReferenceLocked
     */
    this.spatialReferenceLocked = defaultValue(
      options.spatialReferenceLocked,
      false
    )
    /**
     * 地图视图旋转选项。单位为度,默认为0,表示不进行旋转
     * @member {Boolean} MapView.prototype.rotation
     */
    this._rotation = defaultValue(options.rotation, 0)
    /**
     * 试图是否静止
     * @member {Boolean} MapView.prototype.stationary
     * @readonly
     */
    this.stationary = true
    // 是否重置地图视图的坐标系
    this._isCustomCrs = false
    // 是否绑定Leaflet
    this._map._initLeafletView = true
    // 屏幕打印对象
    this._screenshot = null
    // 初始化地图引擎
    // this._initView(false, this._options)
  }

  /**
   * 视点跳转&lt;a id='flyTo'>&lt;/a>
   * @param options 跳转参数
   * @param {Array}  [options.center]  跳转中心点
   * @param {Number}  [options.zoom = 1]  地图层级
   * @param {Extent}  [options.extent]  按范围跳转
   * @example &lt;caption>&lt;h7>中心点跳转示例&lt;/h7>&lt;/caption>
   * // ES5引入方式
   * const { Map, MapView } = Zondy
   * // ES6引入方式
   * import { Map, MapView } from "@mapgis/webclient-leaflet-plugin"
   * // 初始化图层管理容器
   * map = new .Map();
   * // 初始化地图视图对象
   * mapView = new MapView({
   *   // 视图id
   *   viewId: "view-id",
   *   // 图层管理容器
   *   map: map
   * });
   * // 视点跳转
   * mapView.flyTo({
   *   // 跳转中心点
   *   center: [{x}, {y}],
   *   // 地图层级
   *   zoom: {zoom}
   * });
   *
   * @example &lt;caption>&lt;h7>按范围跳转示例&lt;/h7>&lt;/caption>
   * // ES5引入方式
   * const { Map, MapView } = Zondy
   * const { Extent } = Zondy.Geometry
   * // ES6引入方式
   * import { Map, MapView, Extent } from "@mapgis/webclient-leaflet-plugin"
   * // 初始化图层管理容器
   * map = new Map();
   * // 初始化地图视图对象
   * mapView = new MapView({
   *   // 视图id
   *   viewId: "view-id",
   *   // 图层管理容器
   *   map: map
   * });
   * mapView.flyTo({
   *   // 范围几何
   *   extent: new Extent({
   *      "xmin":10,
   *      "xmax":210,
   *      "ymin":0,
   *      "ymax":100,
   *   })
   * });
   *
   * @example &lt;caption>&lt;h7>按范围跳转示例-拿到图层信息后跳转&lt;/h7>&lt;/caption>
   * // ES5引入方式
   * const { IGSMapImageLayer } = Zondy.Layer
   * // ES6引入方式
   * import { IGSMapImageLayer } from "@mapgis/webclient-leaflet-plugin"
   * const igsMapImageLayer = new IGSMapImageLayer({
   *   url: 'http://192.168.82.89:8089/igs/rest/services/Map/Hubei4326/MapServer'
   * });
   * map.add(igsMapImageLayer);
   * // 图层加载完毕
   * igsMapImageLayer.on('layer-view-created', function (result) {
   *   console.log("加载完毕:", result.layer)
   *   //视点跳转
   *   mapView.flyTo({
   *     extent: result.layer.extent
   *   });
   * })
   */
  flyTo(options) {
    options = defaultValue(options, {})
    const zoom = defaultValue(options.zoom, 1)

    // 获取center
    const center = this._getFlyToCenter(options)

    // 获取extent
    const extent = this._getFlyToExtent(options)

    if (this._innerView) {
      if (center) {
        this._innerView.setView(
          [center.coordinates[1], center.coordinates[0]],
          zoom
        )
      } else if (extent) {
        this._innerView.fitBounds([
          [extent.ymin, extent.xmin],
          [extent.ymax, extent.xmax]
        ])
      }
    }
  }

  /**
   * 从extent获取经纬度范围
   * @private
   * @param {Extent} extent 范围对象
   * @return {Object} 经纬度范围
   * */
  _getLatLngFromExtent(extent) {
    const spatialReference = new SpatialReference('EPSG:4326')
    const newExtent = Projection.project(extent, spatialReference)
    const northEastLatlng = new L.latLng(newExtent.ymax, newExtent.xmax)
    const southWestLatlng = new L.latLng(newExtent.ymin, newExtent.xmin)

    return {
      northEastLatlng,
      southWestLatlng
    }
  }

  /**
   * 从center获取经纬度范围
   * @private
   * @param {Point} point 范围对象
   * @return {Object} 地理坐标
   * */
  _getLatLngFromPoint(point) {
    const spatialReference = new SpatialReference('EPSG:4326')
    const newCenter = Projection.project(point, spatialReference)
    const centerLatlng = new L.latLng(
      newCenter.coordinates[1],
      newCenter.coordinates[0]
    )
    return centerLatlng
  }

  /**
   * 初始化地图视图事件
   * @private
   * */
  _initViewEvent() {
    const self = this
    // 定义一个变量存储单击触发事件
    this._clickStore = null
    // 定义一个变量存储拖拽事件参数
    this._dragParams = { isDrag: false, origin: null, action: '', button: null }

    // 地图视图加载完毕事件
    self.fire(ViewEventType.loaded, {})

    // leaflet注册点击事件
    this._innerView.on('click', (event) => {
      // 单击触发事件,300ms延迟
      self._clickStore = setTimeout(function () {
        // 发送左键单击点击事件
        if (self._clickStore) {
          self.fire(ViewEventType.click, MapViewUtil.getMouseEvent(event, self))
        }
      }, 300)
      // 发送立即点击事件
      self.fire(
        ViewEventType.immediateClick,
        MapViewUtil.getMouseEvent(event, self)
      )
    })

    // 注册鼠标右键按下事件
    this._innerView.on('contextmenu', (event) => {
      // 单击触发事件,300ms延迟
      self._clickStore = setTimeout(function () {
        // 发送鼠标右键按下事件
        if (self._clickStore) {
          self.fire(ViewEventType.click, MapViewUtil.getMouseEvent(event, self))
        }
      }, 300)
      // 发送立即点击事件
      self.fire(
        ViewEventType.immediateClick,
        MapViewUtil.getMouseEvent(event, self)
      )
    })

    // 注册双击事件
    this._innerView.on('dblclick', (event) => {
      // 单击事件清理
      if (self._clickStore) {
        clearTimeout(this._clickStore)
        self._clickStore = null
      }
      // 发送双击事件
      self.fire(
        ViewEventType.doubleClick,
        MapViewUtil.getMouseEvent(event, self)
      )
    })

    // 注册鼠标按下事件
    this._innerView.on('mousedown', (event) => {
      // 发送鼠标按下事件
      self.fire(
        ViewEventType.pointerDown,
        MapViewUtil.getMouseEvent(event, self)
      )
      // 发送鼠标拖拽开始事件
      if (!this._dragParams.isDrag) {
        this._dragParams.origin = {
          x: event.originalEvent.layerX,
          y: event.originalEvent.layerY
        }
        this._dragParams.button = event.originalEvent.button
        this._dragParams.action = 'mouse-down'
        this._dragParams.isDrag = false
      }
    })

    // 注册鼠标抬起事件
    this._innerView.on('mouseup', (event) => {
      // 发送鼠标抬起事件
      self.fire(ViewEventType.pointerUp, MapViewUtil.getMouseEvent(event, self))
      // 发送鼠标拖拽结束事件
      if (self._dragParams.isDrag &amp;&amp; this._dragParams.action === 'update') {
        this._dragParams.action = 'end'
        self.fire(
          ViewEventType.drag,
          MapViewUtil.getDragEvent(
            event,
            self,
            self._dragParams.origin,
            this._dragParams.action
          )
        )
      }
      self._dragParams = {
        isDrag: false,
        origin: null,
        action: '',
        button: null
      }
    })

    // 注册鼠标移动事件
    this._innerView.on('mousemove', (event) => {
      // 发送鼠标移动视图事件
      self.fire(
        ViewEventType.pointerMove,
        MapViewUtil.getMouseEvent(event, self)
      )
      // 发送鼠标拖拽中事件
      if (self._dragParams.isDrag) {
        self.fire(
          ViewEventType.drag,
          MapViewUtil.getDragEvent(
            event,
            self,
            self._dragParams.origin,
            self._dragParams.action,
            self._dragParams.button
          )
        )
        self._dragParams.action = 'update'
      } else if (this._dragParams.action === 'mouse-down') {
        this._dragParams.action = 'start'
        self._dragParams.isDrag = true
      }
    })

    // 注册键盘按下事件
    this._innerView.on('keydown', (event) => {
      // 发送键盘按下事件
      self.fire(
        ViewEventType.keyDown,
        MapViewUtil.getKeyEvent(event, self, ViewEventType.keyDown)
      )
    })

    // 注册键盘抬起事件
    this._innerView.on('keyup', (event) => {
      // 发送键盘抬起事件
      self.fire(
        ViewEventType.keyUp,
        MapViewUtil.getKeyEvent(event, self, ViewEventType.keyUp)
      )
    })

    // 注册视图开始改变事件
    this._innerView.on('movestart', () => {
      self.stationary = false
    })

    // 注册zoom变化事件
    this._innerView.on('zoom', (event) => {
      self._map.layers.forEach(function (layer) {
        switch (layer.type) {
          case LayerType.igsFeature:
          case LayerType.graphics:
            showLayerByScale(self, layer)
            break
          default:
            break
        }
      })
      // 发送zoom变化事件
      self.fire(ViewEventType.zoom, {
        zoom: event.target._zoom,
        event
      })
    })
    // 注册zoom end事件
    this._innerView.on('zoomend', (event) => {
      // 更新地图视野范围,比例尺
      self._zoomEndHandler()
      // 发送地图视图改变事件
      self.fire(
        ViewEventType.viewChange,
        MapViewUtil.getViewChangeEvent(event, self)
      )
    })

    // 注册地图移动完成事件
    this._innerView.on('moveend', (event) => {
      // 更新地图视中心
      self._moveEndHandler()
      // 发送地图视图改变事件
      self.fire(
        ViewEventType.viewChange,
        MapViewUtil.getViewChangeEvent(event, self)
      )
      // self.fire(ViewEventType.mapMove, event)
      self.stationary = true
    })

    // 注册地图大小变化事件
    this._innerView.on('resize', (event) => {
      // 发送地图初始化后触发的事件
      self.fire(ViewEventType.resize, {
        oldWidth: event.oldSize.x,
        oldHeight: event.oldSize.y,
        width: event.newSize.x,
        height: event.newSize.y
      })
      // 更新地图窗口宽高
      self._initSize()
    })
  }

  /**
   * 设置(鼠标)操作地图事件是否可用
   * @private
   * @param {String} actionType 操作类型
   * @param {Boolean} isEnable 是否可用
   * */
  _mapActionControl(actionType, isEnable) {
    if (!this._innerView) return
    if (actionType === 'drag-pan') {
      if (isEnable) {
        this._innerView.dragging.enable()
      } else {
        this._innerView.dragging.disable()
      }
    } else if (actionType === 'double-click-zoom') {
      if (isEnable) {
        this._innerView.doubleClickZoom.enable()
      } else {
        this._innerView.doubleClickZoom.disable()
      }
    }
  }

  /**
   * 初始化一个地图视图对象
   * @param isReset 是否是重置视图
   * @private
   * */
  _initView(isReset, options) {
    this._innerView = L.map(this._viewId, {
      // 是否使用Canvas渲染方式
      preferCanvas: this.preferCanvas,
      // 是否显示右下角水印
      attributionControl: defaultValue(options.attributionControl, false),
      // 是否显示缩放控件
      zoomControl: defaultValue(options.zoomControl, true),
      // 是否允许双击鼠标左键缩放或者缩放至图层中心点
      doubleClickZoom: defaultValue(options.doubleClickZoom, true),
      // 是否允许拖拽
      dragging: defaultValue(options.dragging, true),
      // 当使用flyTo缩放至中心点时,缩放级数乘以的系数
      zoomSnap: defaultValue(options.zoomSnap, 1),
      // 当触发zoomIn或者zoomOut操作时,缩放级数乘以的系数
      zoomDelta: defaultValue(options.zoomDelta, 1),
      // 是否允许图层大小随视窗变化
      trackResize: defaultValue(options.trackResize, true),
      // 是否允使用键盘的+/-号,来缩放地图或者使用方向键来平移地图
      keyboard: defaultValue(options.keyboard, true),
      // 使用键盘来平移或缩放地图时的系数,单位px
      keyboardPanDelta: defaultValue(options.keyboardPanDelta, 80),
      // 是否允许使用鼠标滚轮来缩放地图,当值为center时,表示使用滚轮缩放至地图中心点
      scrollWheelZoom: defaultValue(options.scrollWheelZoom, true),
      // 滚轮事件的触发事件,单位毫秒
      wheelDebounceTime: defaultValue(options.wheelDebounceTime, 40),
      // 滚轮缩放时,地图缩放的像素单位,单位像素
      wheelPxPerZoomLevel: defaultValue(options.wheelDebounceTime, 60),
      // 是否开启移动端,手指按压不放事件
      tapHold: defaultValue(options.tapHold, true),
      // 手指有效触发范围,单位像素
      tapTolerance: defaultValue(options.tapTolerance, 15),
      // 是否启用手指两指缩放,当值为center,表示两只滑动,缩放至地图中心
      touchZoom: defaultValue(options.touchZoom, true),
      // 当过最大或最小级数后不再缩放
      bounceAtZoomLimits: defaultValue(options.bounceAtZoomLimits, true),
      // 图层空间参考系
      crs: this.crs,
      // 图层中心点
      center:
        this._center &amp;&amp; this._center.coordinates
          ? [this._center.coordinates[1], this._center.coordinates[0]]
          : [0, 0],
      // 可视范围
      extent: defaultValue(this._extent, undefined),
      // 最大缩放级数
      maxZoom: this.maxZoom,
      // 最小缩放级数
      minZoom: this.minZoom,
      // 初始化时显示级数
      zoom: this._zoom,
      // 是否启用视点跳转动画
      zoomAnimation: this.animation,
      // Config
      config: Config,
      // 是否进行旋转,目前对外没有constraints,默认设置为true
      rotate: true,
      // 旋转bearing,单位为度
      bearing: this._rotation
    })
    // 初始化ui容器
    this.ui = new UI({
      view: this
    })
    // 初始化视图事件
    this._initViewEvent()
    // 初始化可视范围
    if (this._extent &amp;&amp; !isReset) {
      this.flyTo({ extent: this._extent })
    }
    // 初始化比例尺,extent定位优先于scale,center定位
    if (!this._extent &amp;&amp; this._scale &amp;&amp; this._center &amp;&amp; !isReset) {
      this._initScale()
    }
    // 获取视图大小
    this._initSize()
    // 初始化最大比例尺和最小比例尺
    this._initScaleLimit()
    // 初始化地图视图弹框,目前仅支持location方式打开弹框
    this._initPopup()
  }

  // 设置spatialReference
  _setSpatialReference() {
    // 如果basemap有坐标系,则view坐标系取basemap坐标系的值。
    // 没有,则view根据加载的图层动态坐标系的值。
    let spatialReference = null
    let referenceLayer = null
    if (this._map.basemap &amp;&amp; this._map.basemap.spatialReference) {
      spatialReference = this._map.basemap.spatialReference
      referenceLayer = this._map.basemap._referenceLayer
    } else if (this._map.layers) {
      // const target = Utils.getSpatialReference(this._map.layers)
      const target = TileInfoUtil.getSpatialReference(this._map.layers)
      spatialReference = target.spatialReference
      referenceLayer = target.referenceLayer
    }
    // 没有获取到图层参考系,则直接返回
    if (!spatialReference) return
    // 当view上未设置空间参考系锁定,
    // 或空间参考系锁定但没有spatialReference时,则获取新的spatialReference
    if (
      !this.spatialReferenceLocked ||
      (this.spatialReferenceLocked &amp;&amp; !this._spatialReference)
    ) {
      this._spatialReference = spatialReference
      this._referenceLayer = referenceLayer
      // 如果是MapImageLayer,则重置坐标系为当前坐标系
      if (this._spatialReference) {
        const isBasemapLayer = referenceLayer._isBasemapLayer
        TileInfoUtil.setLayerSpatialReference(
          this._map.layers,
          this._spatialReference,
          isBasemapLayer
        )
      }
      // 设置crs
      this.setCrs(referenceLayer)
    }
  }

  setCrs(referenceLayer) {
    const crs = this.getCrsByReferenceLayer(referenceLayer)
    let isNew = false
    let isFirst = false
    if (this.crs) {
      // 比较被设置的新Crs和当前this.crs是否相同。
      // 判断标准 crs.option的resolutions,origin,bounds是否相等。
      if (crs !== null) {
        let newCrsInfo
        if (crs.code.split(':').length > 1) {
          newCrsInfo = {
            wkid: crs.code.split(':')[1]
          }
        } else {
          newCrsInfo = {
            wkid: crs.code
          }
        }
        let currentCrsInfo
        if (this.crs.code.split(':').length > 1) {
          currentCrsInfo = {
            wkid: this.crs.code.split(':')[1]
          }
        } else {
          currentCrsInfo = {
            wkid: this.crs.code
          }
        }

        let isSame
        if (
          newCrsInfo.wkid === undefined ||
          currentCrsInfo.wkid === undefined
        ) {
          isSame = TileInfoUtil.isSameSpatialReference(
            newCrsInfo.wkt,
            currentCrsInfo.wkt
          )
        } else {
          isSame = TileInfoUtil.isSameSpatialReference(
            newCrsInfo.wkid,
            currentCrsInfo.wkid
          )
        }
        if (isSame) {
          // ArcgisMapImage图层临时处理,服务数据extent有问题,暂为解决。4326、3857参考系构建对应的Leaflet参考系对象,
          // 因此没有options,不参与对比bounds、resolutions,暂时组织比较wkid
          // 问题解决后,统一比较 crs.options,该if条件可移除
          if (
            (newCrsInfo.wkid === '4326' || newCrsInfo.wkid === '3857') &amp;&amp;
            !crs.options
          ) {
            isNew = false
          }
          if (crs.options) {
            const newCrsOptions = crs.options
            newCrsInfo.resolutions = newCrsOptions.resolutions
            newCrsInfo.origin = newCrsOptions.origin
            newCrsInfo.bounds = newCrsOptions.bounds
            currentCrsInfo.resolutions = this.crs.options.resolutions
            currentCrsInfo.origin = this.crs.options.origin
            currentCrsInfo.bounds = this.crs.options.bounds
            if (
              JSON.stringify(newCrsInfo.resolutions) !==
                JSON.stringify(currentCrsInfo.resolutions) ||
              JSON.stringify(newCrsInfo.origin) !==
                JSON.stringify(currentCrsInfo.origin) ||
              JSON.stringify(newCrsInfo.bounds) !==
                JSON.stringify(currentCrsInfo.bounds)
            ) {
              isNew = true
            }
          }
        } else {
          isNew = true
        }
      }
    } else if (!this.crs &amp;&amp; crs) {
      isNew = true
      isFirst = true
    }
    if (isNew) {
      this.crs = crs
      if (referenceLayer.extent) {
        const extent = new Extent({
          xmin: referenceLayer.extent.xmin,
          xmax: referenceLayer.extent.xmax,
          ymin: referenceLayer.extent.ymin,
          ymax: referenceLayer.extent.ymax,
          spatialReference: referenceLayer.spatialReference
        })
        const latLng = this._getLatLngFromExtent(extent)
        this._center = new Point({
          coordinates: [
            (latLng.southWestLatlng.lng + latLng.northEastLatlng.lng) / 2,
            (latLng.southWestLatlng.lat + latLng.northEastLatlng.lat) / 2
          ]
        })
      }
      const layers = []
      const baseLayers = []
      this._map.layers.items.forEach((layer) => {
        // 如果是当前图层load完之后触发的重置方法,则不需要重载图层

        // 图层信息已加载完全并且不是第一次设置坐标系,则需要重新加载
        // 图层信息没加载完全并且是第一次设置坐标系,则需要重新加载
        // 图层是第一次设置坐标系,并且是graphicsLayer(不管graphicsLayer是否loaded),则需要重新加载
        // 图层是参考图层,extent没变,则不需要重新加载。图层是参考图层,extent变了,则需要重新加载
        if (this._loadedLayer &amp;&amp; layer.id === this._loadedLayer.id) {
          return
        }
        if ((layer.loaded &amp;&amp; !isFirst) || isFirst) {
          layers.push(layer)
          if (layer._isBasemapLayer) {
            layer._isBasemapLayer = false
            baseLayers.push(layer)
          }
        }
      })
      this._map._canResetView = false
      this._map.removeMany(layers)
      baseLayers.forEach((layer) => {
        layer._isBasemapLayer = true
      })
      this._reCreateView()
      this._map.addMany(layers)
      this._map._canResetView = true
      if (isFirst) {
        referenceLayer.on('layerview-created', () => {
          // 初始化可视范围
          if (this._extent) {
            this.flyTo({ extent: this._extent })
          }
          // 初始化比例尺,extent定位优先于scale,center定位
          if (!this._extent &amp;&amp; this._scale &amp;&amp; this._center) {
            this._initScale()
          }
          // 获取视图大小
          this._initSize()
          // 初始化最大比例尺和最小比例尺
          this._initScaleLimit()
          // 初始化地图视图弹框,目前仅支持location方式打开弹框
          this._initPopup()
        })
      }
    }
  }

  /**
   * 删除当前视图,重新创建一个视图
   * @private
   * */
  _reCreateView() {
    if (this._innerView) {
      this._innerView.remove()
      this._resetExtent()
    }
    this._initView(true, this._options)
  }

  /**
   * 根据crs重新创建extent,center等属性
   * @private
   * */
  _resetExtent() {
    if (this._extent) {
      this._extent = undefined
      this._zoom = 0
    }
  }

  /**
   * 根据图层的spatialReference对象,生成一个坐标系,在leaflet上一定要resolutions、origin、extent以及椭球体参数,
   * 才可以创建一个坐标系,瓦片或者WMTS自带resolutions、origin、extent,
   * 矢量或者WMS需要从extent上计算出resolutions和origin,计算操作在基础图层中完成
   * @private
   * @param {Layer} layer 图层
   * @return {CRS} 地图视图的坐标系对象
   * */
  _getCrs(layer) {
    const spatialReference = layer.spatialReference

    // 投影范围
    const layerExtent = layer.spatialReference.extent || layer.extent
    const extent = Extent.fromJSON(layerExtent.toJSON())

    this._map.layers.forEach((layerItem) => {
      // 如果当前layer是基础底图,则不受普通图层的参考系范围影响,但也会受其他基础底图图层的参考系范围影响
      if (layer._isBasemapLayer &amp;&amp; !layerItem._isBasemapLayer) {
        return
      }
      if (!layerItem.extent) {
        return
      }
      const isSame = TileInfoUtil.isSameSpatialReference(
        layerItem.spatialReference,
        layer.spatialReference
      )
      let layerItemExtent
      if (!isSame) {
        layerItemExtent = Projection.project(layerItem.extent, spatialReference)
      } else {
        layerItemExtent = layerItem.extent
      }
      if (
        layerItem.spatialReference &amp;&amp;
        layer.spatialReference &amp;&amp;
        layerItem.loaded
      ) {
        if (extent.xmin > layerItemExtent.xmin) {
          extent.xmin = layerItemExtent.xmin
        }
        if (extent.ymin > layerItemExtent.ymin) {
          extent.ymin = layerItemExtent.ymin
        }
        if (extent.xmax &lt; layerItemExtent.xmax) {
          extent.xmax = layerItemExtent.xmax
        }
        if (extent.ymax &lt; layerItemExtent.ymax) {
          extent.ymax = layerItemExtent.ymax
        }
      }
    })

    let tileInfo = layer.tileInfo
    // 处理 瓦片图层、arcgis瓦片图层、矢量瓦片图层
    if (layer.type === LayerType.wmts) {
      tileInfo = TileInfoUtil.parseWMTSTileInfo(layer)
    } else if (layer.type === LayerType.wms) {
      tileInfo = TileInfoUtil.parseWMSTileInfo(layer, extent)
    } else if (
      // igsFeature、wfs、geojson图层是矢量图形,图层没有tile size概念,
      // 但Leaflet引擎需要构造分辨率级,给定一个256默认tile size,用于计算分辨率级。
      // igsMapImage图层,如果是图像渲染模式则和上序逻辑相同。如果是瓦片渲染模式,tileSize取图层上设置的瓦片宽高。
      // arcgisMapImage服务对接有暂时问题,暂不自定义计算,直接取arcgisMapImage的tileInfo的分辨率
      layer.type === LayerType.igsMapImage ||
      // layer.type === LayerType.arcgisMapImage ||
      layer.type === LayerType.igsFeature ||
      layer.type === LayerType.wfs ||
      layer.type === LayerType.geojson
    ) {
      tileInfo = TileInfoUtil.parseMapImageTileInfo(layer, extent)
    }
    // igsMapImage图层临时处理,目前根据arcgisMapImage的extent、origin和tileInfo.lods构造出来的参考系有问题。
    // 问题解决后,统一走 tileInfo = TileInfoUtil.parseMapImageTileInfo(layer)逻辑。
    if (layer.type === LayerType.arcgisMapImage) {
      if (
        [3857, 102113, 102100, 900913].indexOf(layer.spatialReference.wkid) > -1
      ) {
        return L.CRS.EPSG3857
      } else if (
        [4326, 4490, 4610, 4214].indexOf(layer.spatialReference.wkid) > -1
      ) {
        return L.CRS.EPSG4326
      } else {
        tileInfo = TileInfoUtil.parseMapImageTileInfo(layer)
      }
    }

    // 原点
    const origin = Point.toCoordinates(tileInfo.origin)
    // 获取分辨率数组
    const resolutions = tileInfo.lods.map((lod) => lod.resolution)
    const length = resolutions.length
    // 如果数组长度小于20,则将数组最后一个除以2,将数组扩充为20,否则地图视图无法继续缩放
    if (length &lt; 20) {
      let lastResolutions = resolutions[length - 1]
      let lastLod = tileInfo.lods[length - 1]
      for (let i = 0; i &lt; 20 - length; i++) {
        resolutions.push(lastResolutions / 2)
        lastLod = {
          resolution: lastResolutions / 2,
          scale: lastLod.scale / 2,
          level: tileInfo.lods.length
        }
        tileInfo.lods.push(lastLod)
        lastResolutions /= 2
      }
    }
    // 创建一个坐标系对象
    return new CRS({
      // 指定wkid,wkid的优先级比wkt高,和igs保持一致
      wkid: spatialReference.wkid,
      // 指定wkt
      wkt: spatialReference.wkt,
      resolutions,
      lods: tileInfo.lods,
      origin,
      extent,
      // 加载矢量瓦片特殊逻辑,需要传入矢量瓦片tileSize(mapboxgl对于初级瓦片的要求),仅影响矢量瓦片显示
      mapboxTileSize: spatialReference._mapboxTileSize
    })
  }

  /**
   * @description 创建图层视图
   * @private
   * @param {Layer} layer 图层
   * @return {LayerView| null}
   */
  _createLayerView(layer) {
    const layerView = getLayerView(this, layer) || null
    return layerView
  }

  /**
   * @description 事件处理器,重载基类方法
   * @private
   */
  _processEvent(event) {
    super._processEvent(event)
    if (event.type === MapEventType.layerRemove) {
      if (
        !this._map._isRemoveMany &amp;&amp;
        !this._map._isRemoveAll &amp;&amp;
        this._map._canResetView
      ) {
        this._setSpatialReference()
      }
    }
    if (
      event.type === MapEventType.layerRemoveMany &amp;&amp;
      this._map._canResetView
    ) {
      if (event.layers) {
        this._setSpatialReference()
      }
    }
  }

  /**
   * @description 通过参考系图层获取CRS对象
   * @private
   * @param {Layer} referenceLayer 图层加载完毕后的对象
   * @return {CRS} CRS对象
   */
  getCrsByReferenceLayer(referenceLayer) {
    let crs = null
    if (
      !referenceLayer ||
      !referenceLayer.spatialReference ||
      !referenceLayer.loaded
    ) {
      return crs
    }
    // 根据基础图层对象上的spatialReference,创建一个自定义坐标系
    // (4326、3857也更具图层信息构造Crs,而不是定义为Leaflet的默认4326、3857Crs)
    crs = this._getCrs(referenceLayer)
    return crs
  }

  /**
   * @description 初始化获取视图尺寸大小
   * @private
   */
  _initSize() {
    const size = this._innerView.getSize()
    this.width = size.x
    this.height = size.y
  }

  /**
   * 等待图层加载完毕,重写BaseView类的该方法
   * @param {Layer} layer 基础图层对象
   * @param {Object} result 图层加载完毕后的对象
   * @param {Function} fireCreatedError 创建失败回调
   * */
  _waitLayerLoaded(layer, result, fireCreatedError) {
    const self = this
    // 是提供坐标系的图层,则加载图层
    if (layer._isSRLayer) {
      const promise = self._addLayer(result, event)
      self._layerLoaded(promise, layer, result, fireCreatedError)
    }
    // 不是的则等待图层加载完毕
    else {
      if (layer._interval) {
        clearInterval(layer._interval)
        layer._interval = undefined
      }
      layer._interval = setInterval(function () {
        const srLayer = self._map._getSRLayer()
        if (!srLayer || (srLayer &amp;&amp; srLayer.loaded)) {
          clearInterval(layer._interval)
          layer._interval = undefined
          const promise = self._addLayer(result, event)
          self._layerLoaded(promise, layer, result, fireCreatedError)
        }
      }, 50)
    }
  }

  /**
   * 销毁视图对象&lt;a id='destroy'>&lt;/a>
   * */
  destroy() {
    this._innerView.remove()
  }

  /**
   * 获取当前视图的中心点(经纬度中心点)&lt;a id='getCenter'>&lt;/a>
   * @return {Object} 中心点对象
   * */
  getCenter() {
    const center = this._innerView.getCenter()
    let centerCrs = new Point({ coordinates: [center.lng, center.lat] })
    this._center = centerCrs
    const spatialReference = new SpatialReference(this.crs.code)
    if (!spatialReference.isGeographic) {
      centerCrs = Projection.project(centerCrs, spatialReference)
    }
    return centerCrs
  }

  /**
   * &lt;a id='setCenter'>&lt;/a>
   * 设置缩视野中心&lt;a id='setCenter'>&lt;/a>
   * @private
   * @param {Point} value 视图中心
   * */
  setCenter(value) {
    if (!this._innerView) return
    if (Array.isArray(value) &amp;&amp; value.length > 1) {
      this._center = new Point({
        coordinates: [value[0], value[1]],
        spatialReference: new SpatialReference(this.crs.code)
      })
      this.flyTo({ zoom: this._zoom, center: this._center })
    } else if (value instanceof Point) {
      this._center = value
      this.flyTo({ zoom: this._zoom, center: this._center })
    }
  }

  /**
   * 获取当前缩放级数&lt;a id='getZoom'>&lt;/a>
   * @return {Number} 当前缩放级数
   * */
  getZoom() {
    this._zoom = this._innerView.getZoom()
    return this._zoom
  }

  /**
   * &lt;a id='setZoom'>&lt;/a>
   * 设置缩放级数
   * @private
   * @param {Number} value 视图层级
   * */
  setZoom(value) {
    if (!this._innerView) return
    this.flyTo({ zoom: value, center: this._center })
  }

  /**
   * 获取最小缩放级数&lt;a id='getMinZoom'>&lt;/a>
   * @return {Number} 最小缩放级数
   * */
  getMinZoom() {
    return this._innerView.getMinZoom()
  }

  /**
   * @description 获取当前视图容器的宽高,单位像素&lt;a id='getSize'>&lt;/a>
   * @return {Object} 当前视图容器的宽高对象
   * */
  getSize() {
    return this._innerView.getSize()
  }

  /**
   * 获取最大缩放级数&lt;a id='getMaxZoom'>&lt;/a>
   * @return {Number} 最大缩放级数
   * */
  getMaxZoom() {
    return this._innerView.getMaxZoom()
  }

  /**
   * 获取当前视图的宽高范围,单位像素&lt;a id='getPixelExtent'>&lt;/a>
   * @return {Object} 视图宽高对象
   * */
  getPixelExtent() {
    return this._innerView.getPixelBounds()
  }

  /**
   * 获取当前视图的像素中心点&lt;a id='getPixelCenter'>&lt;/a>
   * @return {Object} 像素中心点对象
   * */
  getPixelCenter() {
    return this._innerView.getPixelOrigin()
  }

  /**
   * 获取当前视图的像素范围&lt;a id='getPixelWorldExtent'>&lt;/a>
   * @return {Object} 当前视图的像素范围
   * */
  getPixelWorldExtent() {
    return this._innerView.getPixelWorldBounds()
  }

  /**
   * 导出场景视图的配置文件&lt;a id='toJSON'>&lt;/a>
   * @return {Object} 导出的配置文件
   * */
  toJSON() {
    const _json = {}
    _json.extent = toJSON(this._extent, Extent)
    _json.center = toJSON(this._center, Point)
    _json.map = toJSON(this._map, Map)
    _json.viewId = this._viewId
    _json.animation = this.animation
    _json.width = this.width
    _json.height = this.height
    _json.zoom = this._zoom
    _json.scale = this._scale
    _json.maxScale = this.maxScale
    _json.minScale = this.minScale
    _json.preferCanvas = this.preferCanvas
    _json.spatialReferenceLocked = this.spatialReferenceLocked

    return _json
  }

  /**
   * 克隆并返回一个新的场景视图对象&lt;a id='clone'>&lt;/a>
   * @return {MapView} 一个新的场景视图对象
   * */
  clone() {
    return cloneObject(this)
  }

  /**
   * 通过json构造并返回一个新的场景视图对象&lt;a id='fromJSON'>&lt;/a>
   * @param {Object} json json对象
   * @return {MapView} 一个新的场景视图对象
   * */
  static fromJSON(json) {
    return new MapView(json)
  }

  /**
   * &lt;a id='toMap'>&lt;/a>
   * 屏幕像素坐标点转地理坐标点
   * @param {Object} screenPoint 屏幕像素坐标点,例如{ x: 900, y: 500 }
   * @return {Point} 地理坐标点
   * @example &lt;caption>&lt;h5>屏幕像素坐标点转地理坐标示例&lt;/h5>&lt;/caption>
   * // ES6引入方式
   * import { MapView } from "@mapgis/webclient-leaflet-plugin"
   * import { Map, Point, Extent } from "@mapgis/webclient-common"
   * // 初始化图层管理容器
   * const map = new Map();
   * // 初始化地图视图对象
   * const mapView = new MapView({
   *   // 视图id
   *   viewId: "mapgis-2d-viewer",
   *   // 图层管理容器
   *   map: map
   * });
   * const screenPoint = { x: 900, y: 500 }
   * mapView.toMap(screenPoint)
   * */
  toMap(screenPoint) {
    const latLngPoint = this._innerView.containerPointToLatLng(screenPoint)
    let geoPoint = new Point({
      coordinates: [latLngPoint.lng, latLngPoint.lat]
    })
    const spatialReference = new SpatialReference(this.crs.code)
    if (!spatialReference.isGeographic) {
      geoPoint = Projection.project(geoPoint, spatialReference)
    } else {
      geoPoint.spatialReference = spatialReference
    }
    return geoPoint
  }

  /**
   * &lt;a id='toScreen'>&lt;/a>
   * 地理坐标点转屏幕像素坐标点
   * @param  {Point}point 地理坐标点
   * @return {Object} 屏幕像素坐标点
   * @example &lt;caption>&lt;h5>地理坐标点转屏幕像素坐标示例&lt;/h5>&lt;/caption>
   * // ES6引入方式
   * import { MapView } from "@mapgis/webclient-leaflet-plugin"
   * import { Map, Point, Extent } from "@mapgis/webclient-common"
   * // 初始化图层管理容器
   * const map = new Map();
   * // 初始化地图视图对象
   * const mapView = new MapView({
   *   // 视图id
   *   viewId: "mapgis-2d-viewer",
   *   // 图层管理容器
   *   map: map
   * });
   * const geoPoint = new Point({ coordinates: [123, 23, 0] })
   * mapView.toScreen(geoPoint)
   * */
  toScreen(point) {
    let _point = Geometry.fromJSON(point)
    if (!_point.spatialReference.isGeographic) {
      _point = Projection.project(
        _point,
        new SpatialReference({
          wkid: 4326
        })
      )
    }
    const screenPoint = this._innerView.latLngToContainerPoint([
      _point.coordinates[1],
      _point.coordinates[0]
    ])
    return screenPoint
  }

  /**
   * &lt;a id='takeScreenshot'>&lt;/a>
   * @description 屏幕快照
   * @param {Object} [options = {}] 屏幕快照配置配置
   * @param {PictureFormat} [options.format = PictureFormat.png] 照片格式,支持png,jpeg格式
   * @param {String} [options.filename = 'screenshotFile'] 下载文件名
   * @param {Number} [options.width = undefined] 图片宽度
   * @param {Number} [options.height = undefined] 图片高度
   * @param {Number} [options.x = undefined] 图片原点x
   * @param {Number} [options.y = undefined] 图片原点y
   * @param {Boolean} [options.isDownload = true] 是否下载图片
   * @return {Object} 屏幕快照 {dataUrl String },且浏览器会下载图片
   * @example &lt;caption>&lt;h7>屏幕快照&lt;/h7>&lt;/caption>
   * // ES5引入方式
   * const { Map, MapView } = Zondy
   * const { PictureFormat } = Zondy.Enum
   * // ES6引入方式
   * import { Map, MapView } from "@mapgis/webclient-leaflet-plugin"
   * import { PictureFormat } from "@mapgis/webclient-common"
   * // 初始化图层管理容器
   * const map = new Map();
   * // 初始化地图视图对象
   * const mapView = new MapView({
   *   // 二维场景视图的容器(html的div标签)ID
   *   viewId: "二维场景视图的容器的id",
   *   // 图层管理容器
   *   map: map
   * })
   * // 设置屏幕快照参数
   * const screenshotOptions: {
   *    format: PictureFormat.png
   * }
   * // 开始屏幕快照
   * mapView.takeScreenshot(screenshotOptions).then((result) => {
   *   // 获取base64格式的url字符串
   *   console.log("dataUrl:", result.dataUrl)
   * })
   * */
  takeScreenshot(options) {
    options.view = this
    let screenshotResult = null
    if (!this._screenshot) {
      this._screenshot = new Screenshot(options)
      screenshotResult = this._screenshot._addView()
    } else {
      screenshotResult = this._screenshot._addView()
    }
    return screenshotResult
  }

  /**
   * 获取当前视图的地理范围&lt;a id='getExtent'>&lt;/a>
   * @return {Extent}  获取当前视图的地理范围
   * */
  getExtent() {
    if (!this._innerView) return
    const latLngBounds = this._innerView.getBounds()
    let extent = new Extent({
      xmin: latLngBounds._southWest.lng,
      ymin: latLngBounds._southWest.lat,
      xmax: latLngBounds._northEast.lng,
      ymax: latLngBounds._northEast.lat
    })
    const spatialReference = new SpatialReference(this.crs.code)
    if (!spatialReference.isGeographic) {
      extent = Projection.project(extent, spatialReference)
    }
    this._extent = extent
    return extent
  }

  /**
   * &lt;a id='setExtent'>&lt;/a>
   * 设置视野范围
   * @private
   * @param {Extent} value 范围对象
   * */
  setExtent(value) {
    if (!this._innerView) return
    this._extent = value
    this.flyTo({ extent: value })
  }

  /**
   * zoom end事件处理器
   * @private
   * */
  _zoomEndHandler() {
    // 更新地图视野范围
    this.getExtent()
    // 更新地图层级
    this.getZoom()
    // 更新地图比例尺
    this.getScale()
    this.getResolution()
  }

  /**
   * move end事件处理器
   * @private
   * */
  _moveEndHandler() {
    // 更新地图视野中心
    this.getCenter()
    // 更新地图视野范围
    this.getExtent()
  }

  /**
   * 初始化最大比例尺和最小比例尺
   * @private
   * */
  _initScaleLimit() {
    if (this.minScale) {
      const result = this._getScaleZoom(this.minScale)
      if (result.zoom) {
        this.maxZoom = result.zoom
        this._innerView.setMaxZoom(this.maxZoom)
      }
    }
    if (this.maxScale) {
      const result = this._getScaleZoom(this.maxScale)
      if (result.zoom) {
        this.minZoom = result.zoom
        this._innerView.setMinZoom(this.minZoom)
      }
    }
    if (
      !this._scale ||
      this._scale &lt;= this.minScale ||
      this._scale >= this.minScale
    ) {
      this._scale = this.maxScale || this.minScale
      this.setScale(this._scale)
    }
  }

  /**
   * 初始化弹窗
   * @private
   * */
  _initPopup() {
    this.popup = new Popup({ view: this })
  }

  /**
   * &lt;a id='hitTest'>&lt;/a>
   * @description 穿透检测,图元拾取。目前支持graphic类型拾取结果,支持图层类型GraphicLayer,FeatureLayer。
   * @param {Object} screenPoint  屏幕像素坐标点,例如{ x: 900, y: 500 }
   * @return {Array} 图元检测结果
   * @example &lt;caption>&lt;h7>根据基础图层对象或者图层id查询并返回实际图层&lt;/h7>&lt;/caption>
   * // ES6引入方式
   * import { MapView } from "@mapgis/webclient-leaflet-plugin";
   * import { Map, Point, Polygon, MultiPolygon ,Extent, GraphicsLayer, Feature, Circle, IGSFeatureLayer, IGSTileLayer } from "@mapgis/webclient-common";

   * // 初始化图层管理容器
   * const map = new Map();
   * // 初始化地图视图对象
   * this.mapView = new MapView({
   *   // 视图id
   *   viewId: "mapgis-2d-viewer",
   *   // 图层管理容器
   *   map: map,
   * })
   * // 创建一个要素
   * const feature = [
   *   new Feature({
   *     id: '11113',
   *     geometry: new Circle({
   *       center: [113, 35],
   *       radius: 10000,
   *       radiusUnit: 'kilometers',
   *     })
   *   }),
   *   new Feature({
   *     id: '11114',
   *     geometry: new Polygon({
   *       coordinates: [
   *         // 外圈
   *         [
   *           [113.0, 29.0],
   *           [116.0, 29.0],
   *           [116.0, 35.0],
   *           [113.0, 35.0],
   *           [113.0, 29.0]
   *         ]
   *       ]
   *     })
   *   }),
   *   new Feature({
   *     id: '11115',
   *     geometry:new MultiPolygon({
   *       coordinates: [
   *         [
   *           // 外圈
   *           [
   *             [112.0, 28.0],
   *             [115.0, 28.0],
   *             [115.0, 30.0],
   *             [112.0, 30.0],
   *             [112.0, 28.0]
   *           ],
   *           // 第一个内圈
   *           [
   *             [112.2, 28.2],
   *             [112.2, 29.8],
   *             [114.8, 29.8],
   *             [114.8, 28.2],
   *             [112.2, 28.2]
   *           ]
   *         ]
   *       ]
   *     })
   *   })
   * ]
   * // 初始化几何图层
   * const graphicsLayer = new GraphicsLayer({
   *   graphics:feature
   * })
   * map.add(this.graphicsLayer)
   * const result = this.mapView.hitTest({x:1100,y:600})
   * */
  hitTest(screenPoint) {
    let hitTestResult = []
    if (this.preferCanvas) {
      hitTestResult = this._hitTestCanvas(screenPoint)
    } else {
      hitTestResult = this._hitTestSVG(screenPoint)
    }
    return hitTestResult
  }

  /**
   * SVG渲染方式下图元拾取
   * @private
   * @param {Object} screenPoint  屏幕像素坐标点,例如{ x: 900, y: 500 }
   * @return {Array} SVG渲染方式下图元检测结果
   * */
  _hitTestSVG(screenPoint) {
    const self = this
    const pickedInnerLayer = []
    const clientRect = this._innerView._container.getBoundingClientRect()
    const elements = document.elementsFromPoint(
      clientRect.x + screenPoint.x,
      clientRect.y + screenPoint.y
    )
    elements.forEach((el) => {
      if (el.classList.contains('leaflet-interactive') &amp;&amp; el._leaflet_id) {
        pickedInnerLayer.push(self._innerView._targets[el._leaflet_id])
      }
    })
    const hitTestResult = []
    pickedInnerLayer.forEach((innerLayer) => {
      const idRes = this._getInnerLayerId(innerLayer)
      if (isNull(idRes.commonLayerId)) {
        return
      }
      const layer = this.getLayer(idRes.commonLayerId)
      let features = null
      if (
        layer.type === LayerType.igsFeature ||
        layer.type === LayerType.geojson
      ) {
        const layerView = this._getLayerView(layer)
        features = layerView._featureSetCache.filter((item) => {
          if (item.id === idRes.commonFeatureId) {
            return item
          }
        })
      } else if (layer.type === LayerType.graphics) {
        features = layer.graphics.filter((item) => {
          if (item.id === idRes.commonFeatureId) {
            return item
          }
        })
      }
      const feature =
        features &amp;&amp; features.items.length > 0 ? features.items[0] : null
      const graphicHit = {
        graphic: feature,
        layer,
        mapPoint: this.toMap(screenPoint),
        type: 'graphic'
      }
      hitTestResult.push(graphicHit)
    })
    return hitTestResult
  }

  /**
   * Canvas渲染方式下图元拾取
   * @private
   * @param {Object} screenPoint  屏幕像素坐标点,例如{ x: 900, y: 500 }
   * @return {Array} Canvas渲染方式下图元检测结果
   * */
  _hitTestCanvas(screenPoint) {
    const pickedInnerLayer = []
    const renderer = this._innerView._renderer
    const e = { clientX: screenPoint.x, clientY: screenPoint.y }
    const point = renderer._map.mouseEventToContainerPoint(e)
    for (let order = renderer._dragFirst; order; order = order.next) {
      const layer = order.layer
      if (layer.options.interactive &amp;&amp; layer._containsPoint(point)) {
        if (
          !(e.type === 'click' || e.type === 'preclick') ||
          !renderer._map._draggableMoved(layer)
        ) {
          if (layer.feature) {
            pickedInnerLayer.push(layer)
          }
        }
      }
    }
    const hitTestResult = []
    pickedInnerLayer.forEach((innerLayer) => {
      const idRes = this._getInnerLayerId(innerLayer)
      if (isNull(idRes.commonLayerId)) {
        return
      }
      const layer = this.getLayer(idRes.commonLayerId)
      let features = null
      if (
        layer.type === LayerType.igsFeature ||
        layer.type === LayerType.geojson
      ) {
        const layerView = this.getLayerView(layer)
        features = layerView._featureSetCache.filter((item) => {
          if (item.id === idRes.commonFeatureId) {
            return item
          }
        })
      } else if (layer.type === LayerType.graphics) {
        features = layer.graphics.filter((item) => {
          if (item.id === idRes.commonFeatureId) {
            return item
          }
        })
      }
      const feature = features.items.length > 0 ? features.items[0] : null
      const graphicHit = {
        graphic: feature,
        layer,
        mapPoint: this.toMap(screenPoint),
        type: 'graphic'
      }
      hitTestResult.push(graphicHit)
    })
    return hitTestResult
  }

  // 递归获取引擎图层对象上的layerId和featureId
  _getInnerLayerId(innerLayer) {
    let idObj
    const getParentLayerId = function (eventParents) {
      let layerId
      let featureId
      for (const i in eventParents) {
        if (eventParents[i].commonLayerId === undefined) {
          layerId = getParentLayerId(eventParents[i]._eventParents)
        } else {
          layerId = eventParents[i].commonLayerId
          featureId = eventParents[i].commonFeatureId
          break
        }
      }
      return { commonLayerId: layerId, commonFeatureId: featureId }
    }
    if (innerLayer.commonLayerId === undefined) {
      idObj = getParentLayerId(innerLayer._eventParents)
    } else {
      idObj = {
        commonLayerId: innerLayer.commonLayerId,
        commonFeatureId: innerLayer.commonFeatureId
      }
    }
    return idObj
  }

  /**
   * 初始化地图视图比例尺
   * @private
   * */
  _initScale() {
    if (this._scale) {
      if (this.minScale &amp;&amp; this._scale > this.minScale) {
        this.setScale()
      }
      if (this.maxScale &amp;&amp; this._scale &lt; this.maxScale) {
        this.setScale()
      }
    }
  }

  _getScaleZoom(value) {
    let zoom = undefined
    let scale = undefined
    if (this.crs &amp;&amp; this.crs.options &amp;&amp; this.crs.options.lods) {
      for (let i = 0; i &lt; this.crs.options.lods.length; i++) {
        const item = this.crs.options.lods[i]
        const lastItem = this.crs.options.lods[i - 1]
        if (i === 0) {
          if (value >= item.scale) {
            zoom = item.level
            scale = item.scale
            break
          }
        } else if (i === this.crs.options.lods.length - 1) {
          if (value &lt;= item.scale) {
            zoom = item.level
            scale = item.scale
            break
          }
        } else if (value >= item.scale &amp;&amp; value &lt;= lastItem.scale) {
          zoom = item.level
          scale = item.scale
          break
        }
      }
    }
    return { zoom, scale }
  }

  /**
   * 地图比例尺转视野范围
   * @private
   * @param {Number}  scale 比例尺
   * @return {Extent}  视野范围
   * */
  _getScaleExtent(scale) {
    const extent = this._getScaleDefaultExtent(scale)
    return extent
  }

  /**
   * 地图比例尺转默认坐标系的视野范围
   * @private
   * @param {Number}  scale 比例尺
   * @return {Extent}  视野范围
   * */
  _getScaleDefaultExtent(scale) {
    const ppi = 96 // 1 inch=2.54 厘米(cm)
    const size = this.getSize()
    const lengthX = (size.x / ppi) * 0.0254 * scale // 米
    const lengthY = (size.y / ppi) * 0.0254 * scale
    const length = Math.sqrt(lengthX * lengthX + lengthY * lengthY)
    const absAngle = (180 / Math.PI) * Math.atan2(lengthX, lengthY)

    const center = this._center.coordinates
    const getTargetPoint = (point, distance, bearing) => {
      point = T.point(point)
      const options = { units: 'kilometers' }
      const destination = T.destination(point, distance, bearing, options)
      return destination.geometry.coordinates
    }
    const leftTopPoint = getTargetPoint(center, length / 2000, -absAngle)
    const rightBottomPoint = getTargetPoint(
      center,
      length / 2000,
      180 - absAngle
    )
    const extent = new Extent({
      xmin: leftTopPoint[0],
      xmax: rightBottomPoint[0],
      ymin: rightBottomPoint[1],
      ymax: leftTopPoint[1]
    })
    return extent
  }

  /**
   * &lt;a id='getScale'>&lt;/a>
   * 获取当前比例尺
   * @return {Number}  比例尺 实际10000米:地图1米
   * */
  getScale0() {
    const ppi = 96 // 1 inch=2.54 厘米(cm)
    const size = this.getSize()
    const screenX = (size.x / ppi) * 0.0254
    const screenY = (size.y / ppi) * 0.0254
    const length = Math.sqrt(screenX * screenX + screenY * screenY)
    const latLngBounds = this._innerView.getBounds()
    const southWestPoint = T.point([
      latLngBounds._southWest.lng,
      latLngBounds._southWest.lat
    ])
    const northEastPoint = T.point([
      latLngBounds._northEast.lng,
      latLngBounds._northEast.lat
    ])
    const options = { units: 'kilometers' }
    const distance = T.distance(southWestPoint, northEastPoint, options)
    const scale = (distance * 1000) / length
    this._scale = scale
    return scale
  }

  getScale() {
    let scale = 0
    if (this.crs &amp;&amp; this.crs.options &amp;&amp; this.crs.options.lods) {
      if (!isNull(this._zoom)) {
        if (this._zoom % 1 === 0) {
          const lod = this.crs.options.lods[this._zoom]
          scale = lod.scale
        } else {
          const floorZoom = Math.floor(this._zoom)
          const ceilZoom = Math.ceil(this._zoom)
          const floorLod = this.crs.options.lods[floorZoom]
          const ceilLod = this.crs.options.lods[ceilZoom]
          if (floorLod === undefined &amp;&amp; ceilLod === undefined) {
            scale = 0
          } else if (floorLod === undefined) {
            scale = ceilLod.scale
          } else if (ceilLod === undefined) {
            scale = floorLod.scale
          } else {
            scale =
              floorLod.scale -
              (floorLod.scale - ceilLod.scale) * (this._zoom - floorZoom)
          }
        }
      }
    }
    this._scale = scale
    return scale
  }

  /**
   * 通过视野范围参数 获取当前比例尺
   * @param {Extent}  extent 比例尺
   * @return {Number}  比例尺 实际10000米:地图1米
   * @private
   * */
  _getScaleByExtent(extent) {
    const ppi = 96 // 1 inch=2.54 厘米(cm)
    const size = this.getSize()
    const screenX = (size.x / ppi) * 0.0254
    const screenY = (size.y / ppi) * 0.0254
    const length = Math.sqrt(screenX * screenX + screenY * screenY)
    const latLngBounds = this._getLatLngFromExtent(extent)
    const southWestPoint = T.point([
      latLngBounds.southWestLatlng.lng,
      latLngBounds.southWestLatlng.lat
    ])
    const northEastPoint = T.point([
      latLngBounds.northEastLatlng.lng,
      latLngBounds.northEastLatlng.lat
    ])
    const options = { units: 'kilometers' }
    const distance = T.distance(southWestPoint, northEastPoint, options)
    const scale = (distance * 1000) / length
    this._scale = scale
    return scale
  }

  /**
   * &lt;a id='setScale'>&lt;/a>
   * 设置当前比例尺
   * @private
   * @param {Number} value 比例尺 实际10000米:地图1米
   * */
  setScale(value) {
    if (!this._innerView) return
    const result = this._getScaleZoom(value)
    // 视图定位
    if (result.zoom) {
      this.flyTo({ center: this._center, zoom: result.zoom + 1 })
    }
  }

  /**
   * 获取视图范围的比例尺
   * @private
   * @param {Extent} extent 范围对象
   * @return {Object} 比例尺
   * */
  getExtentScale(extent) {
    const ppi = 96 // 1 inch=2.54 厘米(cm)
    const size = this.getSize()
    const screenX = (size.x / ppi) * 0.0254
    const screenY = (size.y / ppi) * 0.0254
    const length = Math.sqrt(screenX * screenX + screenY * screenY)
    const leftTopPoint = T.point([extent.xmin, extent.ymax])
    const rightBottomPoint = T.point([extent.xmax, extent.ymin])
    const options = { units: 'kilometers' }
    const distance = T.distance(leftTopPoint, rightBottomPoint, options)
    const scale = (distance * 1000) / length
    this._scale = scale
    return scale
  }

  getResolutionByScale(scale) {
    const ppi = 96
    this._resolution = (0.0254 / ppi) * scale
    return this._resolution
  }

  getResolution() {
    const size = this.getSize()
    const length = Math.sqrt(size.x * size.x + size.y * size.y)
    const latLngBounds = this._innerView.getBounds()
    const southWestPoint = T.point([
      latLngBounds._southWest.lng,
      latLngBounds._southWest.lat
    ])
    const northEastPoint = T.point([
      latLngBounds._northEast.lng,
      latLngBounds._northEast.lat
    ])
    const options = { units: 'kilometers' }
    const distance = T.distance(southWestPoint, northEastPoint, options)
    this._resolution = (distance * 1000) / length
    return this._resolution
  }
}

/**
 * 通过一个配置生成一个场景视图对象
 * @param {Object} json 场景视图配置
 * @return {MapView}
 * */
MapView.fromJSON = function (json) {
  json = defaultValue(json, {})

  return new MapView(json)
}

Object.defineProperties(MapView.prototype, {
  scale: {
    get() {
      return this._scale
    },
    set(value) {
      this.setScale(value)
    }
  },
  zoom: {
    get() {
      return this._zoom
    },
    set(value) {
      this.setZoom(value)
    }
  },
  extent: {
    get() {
      return this._extent
    },
    set(value) {
      this.setExtent(value)
    }
  },
  center: {
    get() {
      let center = this._center
      if (this.crs) {
        const spatialReference = new SpatialReference(this.crs.code)
        if (!spatialReference.isGeographic) {
          center = Projection.project(this._center, spatialReference)
        }
      }
      return center
    },
    set(value) {
      this.setCenter(value)
    }
  },
  spatialReference: {
    get() {
      return this._spatialReference
    },
    set(value) {
      this._spatialReference = value
    }
  },
  rotation: {
    get() {
      return this._rotation
    },
    set(value) {
      this._rotation = value
      if (this._innerView) {
        this._innerView.setBearing(value)
      }
    }
  }
})
Zondy.PluginVersion = '16.5.2'
Zondy.MapView = MapView
export default MapView
</code></pre>
        </article>
    </section>




            </div>
            
            <footer class="footer">
                <div class="content has-text-centered">
                    <p>文档生成<a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a></p>
                    <p class="sidebar-created-by">
                        <a href="http://www.smaryun.com" target="_blank">司马云</a> <span>© 2023 云生态圈</span>
                        <a href="http://192.168.82.89:8086/#/index" target="_blank">MapGIS Client for JavaScript</a>
                    </p>
                </div>
            </footer>
            
        </div>
        <div id="side-nav" class="side-nav">
            <div style="margin-bottom: 10px;">
                <img style="vertical-align: middle;margin-right: 10px;width: 30px;" src="./styles/anchor.png"/><a href="#page-title">构造函数</a>
            </div>
            <div style="margin-bottom: 10px;">
                <img style="vertical-align: middle;margin-right: 10px;width: 30px;" src="./styles/anchor.png"/><a href="#member">成员变量</a>
            </div>
            <div style="margin-bottom: 10px;">
                <img style="vertical-align: middle;margin-right: 10px;width: 30px;" src="./styles/anchor.png"/><a href="#function">方法</a>
            </div>
            <div>
                <img style="vertical-align: middle;margin-right: 10px;width: 30px;" src="./styles/anchor.png"/><a href="#event">事件</a>
            </div>
        </div>
    </div>
<script src="scripts/linenumber.js"> </script>

<script src="scripts/search.js"> </script>

</body>
</html>