/// import Event from '../event' import DistMgr from './DistMgr' import DistCounter from './DistCounter' import { BaseRender } from './BaseRender' import PointItem from './PointItem' import BoundsItem from './BoundsItem' import utils from './utils' import type { RenderOptions } from './BaseRender' export type { RenderOptions, StyleOption, FeatureStyleByLevelOption } from './BaseRender' export interface DistrictClusterOptions { map: AMap.Map // 地图实例 zIndex?: number // 默认10 visible?: boolean // 是否显示 data: any[] // 数据源数组,每个元素即为点相关的信息 getPosition: (dataItem: any, dataIndex: number) => AMap.LngLatLike // 透明度,默认1 autoSetFitView?: boolean // 是否在绘制后自动调整地图视野以适合全部点,默认true topAdcodes?: number[] // 顶层区划的adcode列表。默认为[100000],即全国范围.假如仅需要展示河北和北京,可以设置为[130000, 110000] excludedAdcodes?: number[] // 需要排除的区划的adcode列表 renderOptions: RenderOptions } type _OptOptions = Required class DistrictCluster extends Event { _opts: _OptOptions //初始化参数 map: AMap.Map // 地图实例 _distMgr: DistMgr _distCounter: DistCounter renderEngine: BaseRender _data = { list: [], bounds: null, source: null } as any _mouseEvent = utils.bind( utils.debounce(() => { this.renderLater() }, 50), this ) constructor(options: DistrictClusterOptions) { super() this.initCSS() const defaultOptions = { autoSetFitView: true, topAdcodes: [100000], visible: true, excludedAdcodes: null, zIndex: 10, renderOptions: {} } this._opts = utils.extend({}, defaultOptions, options) this.map = options.map this._distMgr = new DistMgr({ topAdcodes: this._opts.topAdcodes, excludedAdcodes: this._opts.excludedAdcodes }) this._distCounter = new DistCounter({ distMgr: this._distMgr, pointPackerThisArg: this, pointPacker: (p) => { return this._packDataItem(p) } }) this.renderEngine = new BaseRender(this, { ...options.renderOptions, zIndex: this._opts.zIndex, visible: this._opts.visible, map: options.map }) this.renderEngine.on('*', (name, ...data: any[]) => { this.emit(name as any, ...data) }) this._opts.data && this.setData(this._opts.data) this.bindOrUnbindMapEvent() } bindOrUnbindMapEvent(bind = true) { const method = bind ? 'on' : 'off' this.map[method]('moveend', this._mouseEvent) this.map[method]('zoomend', this._mouseEvent) this.map[method]('resize', this._mouseEvent) this.map[method]('rotateend', this._mouseEvent) this.map[method]('dragend', this._mouseEvent) } initCSS() { const id = '_amap_district_cluster_css' const style = document.getElementById(id) if (style) { return } const css = ".amap-ui-district-cluster-container{cursor:default;-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0) scale(1,1)}.amap-ui-district-cluster-container canvas{position:absolute}.amap-ui-district-cluster-container .amap-ui-hide{display:none!important}.amap-ui-district-cluster-container .overlay-title,.amap-ui-district-cluster-marker{color:#555;background-color:#fffeef;font-size:12px;white-space:nowrap;position:absolute}.amap-ui-district-cluster-container .overlay-title{padding:2px 6px;display:inline-block;z-index:99999;border:1px solid #7e7e7e;border-radius:2px}.amap-ui-district-cluster-container .overlay-title:after,.amap-ui-district-cluster-container .overlay-title:before{content:'';display:block;position:absolute;margin:auto;width:0;height:0;border:solid transparent;border-width:5px}.amap-ui-district-cluster-container .overlay-title.left{transform:translate(10px,-50%)}.amap-ui-district-cluster-container .overlay-title.left:before{top:5px}.amap-ui-district-cluster-container .overlay-title.left:after{left:-9px;top:5px;border-right-color:#fffeef}.amap-ui-district-cluster-container .overlay-title.left:before{left:-10px;border-right-color:#7e7e7e}.amap-ui-district-cluster-container .overlay-title.top{transform:translate(-50%,-130%)}.amap-ui-district-cluster-container .overlay-title.top:before{left:0;right:0}.amap-ui-district-cluster-container .overlay-title.top:after{bottom:-9px;left:0;right:0;border-top-color:#fffeef}.amap-ui-district-cluster-container .overlay-title.top:before{bottom:-10px;border-top-color:#7e7e7e}.amap-ui-district-cluster-marker{border:1px solid #8e8e8e;width:auto;height:22px;border-radius:5px 5px 5px 0;left:0;top:0}.amap-ui-district-cluster-marker:after,.amap-ui-district-cluster-marker:before{content:'';display:block;position:absolute;width:0;height:0;border:solid rgba(0,0,0,0);border-width:6px;left:13px}.amap-ui-district-cluster-marker:after{bottom:-12px;border-top-color:#fffeef}.amap-ui-district-cluster-marker:before{bottom:-13px;border-top-color:#8e8e8e}.amap-ui-district-cluster-marker span{vertical-align:middle;padding:3px 5px;display:inline-block;height:16px;line-height:16px}.amap-ui-district-cluster-marker-title{border-radius:5px 0 0 0}.amap-ui-district-cluster-marker-body{background-color:#dc3912;color:#fff;border-radius:0 5px 5px 0}.amap-ui-district-cluster-marker.level_country .amap-ui-district-cluster-marker-body{background-color:#36c}.amap-ui-district-cluster-marker.level_province .amap-ui-district-cluster-marker-body{background-color:#dc3912}.amap-ui-district-cluster-marker.level_city .amap-ui-district-cluster-marker-body{background-color:#909}.amap-ui-district-cluster-marker.level_district .amap-ui-district-cluster-marker-body{background-color:#d47}" const d = document, a = 'appendChild', i = 'styleSheet', s = d.createElement('style') s.id = id s.type = 'text/css' d.getElementsByTagName('head')[0][a](s) s[i] ? (s[i].cssText = css) : s[a](d.createTextNode(css)) } getMinZoomToShowSub(adcode) { return this.renderEngine.getMinZoomToShowSub(adcode) } getAreaNodeProps(adcode) { return this._distMgr.getNodeByAdcode(adcode) } getDistrictExplorer() { return this._distMgr.getExplorer() } getRender() { return this.renderEngine } zoomToShowSubFeatures(adcode, center?: any) { this.renderEngine.zoomToShowSubFeatures(adcode, center) } renderLater(time?: number) { this.renderEngine.renderLater(time) } render() { this.renderEngine.render() } forceRender() { this.renderEngine.forceRender() } getDistMgr() { return this._distMgr } _clearData() { this.trigger('willClearData') this._data ? (this._data.list.length = 0) : (this._data = { list: [], bounds: null }) this._data.source = null this._data.bounds = null this._data.kdTree = null this._distCounter.clearData() this.trigger('didClearData') } _buildDataItems(data) { const opts = this._opts, posGetter = opts.getPosition, list = this._data.list, bounds = this._data.bounds for (let idx = 0, len = data.length; idx < len; idx++) { let point = data[idx], lngLat = posGetter.call(this, point, idx) as any if (lngLat) { lngLat.getLng && (lngLat = [lngLat.getLng(), lngLat.getLat()]) list[idx] = new PointItem(lngLat[0], lngLat[1], idx) bounds.expandByPoint(lngLat[0], lngLat[1]) } } } getDataItemsByBounds(bounds) { const kdTree = this._data.kdTree if (!kdTree) return null const min = bounds.getSouthWest(), max = bounds.getNorthEast(), list = this._data.list, idxList = kdTree.range(min.getLng(), min.getLat(), max.getLng(), max.getLat()), dataItems: any[] = [] for (let i = 0, len = idxList.length; i < len; i++) dataItems[i] = this._packDataItem(list[idxList[i]]) return dataItems } _packDataItem(pointItem) { if (!pointItem) return null if (!pointItem._packedItem) { const idx = pointItem.idx, position = [pointItem.x, pointItem.y] pointItem._packedItem = { dataIndex: idx, dataItem: this._data.source[idx], position } } return pointItem._packedItem } _buildData(data) { this._clearData() this.trigger('willBuildData', data) this._data.source = data this._data.bounds = BoundsItem.getBoundsItemToExpand() this._buildDataItems(data) this._distCounter.setData(this._data.list) this.trigger('didBuildData', data) } setData(data) { data || (data = []) this._buildData(data) this.forceRender() data.length && this._opts.autoSetFitView && this.setFitView() } isReady() { return this._distMgr.isReady() && !!this._data } setFitView() { const nodeBounds = this._data.bounds, map = this.getMap(), mapBounds = new AMap.Bounds( [nodeBounds.x, nodeBounds.y], [nodeBounds.x + nodeBounds.width, nodeBounds.y + nodeBounds.height] ) map && map.setBounds(mapBounds) } getDistCounter() { return this._distCounter } getMap(): any { return this._opts.map } getZooms() { return this.renderEngine.getZooms() } isHidden() { return !this._opts.visible } show() { this._opts.visible = true return this.getRender().show() } hide() { this._opts.visible = false return this.getRender().hide() } destroy() { this.bindOrUnbindMapEvent(false) this.getRender().destroy() this._distCounter.destroy() this._distMgr.destroy() this.renderEngine = null as any this._data = { list: [], bounds: null } this._distMgr = null as any this.map = undefined as any this._opts = undefined as any } getzIndex(): number { return this._opts.zIndex as number } setzIndex(zIndex: number) { this._opts.zIndex = zIndex this.getRender().setzIndex(zIndex) } } export { DistrictCluster }