/// import { polygon } from '@turf/helpers' import intersect from '@turf/intersect' import utils from './utils' import DistrictExplorer from './DistrictExplorer' import BoundsItem from './BoundsItem' import SphericalMercator from './SphericalMercator' interface DeepCount { total: number count: number } export default class DistMgr { _opts: any _touchMap: any singleCountryNode: any isDistReady = false nodeMap = {} waitFnList: any[] = [] singleDistExplorer = new DistrictExplorer({}) constructor(opts) { this._opts = utils.extend( { topAdcodes: [1e5] }, opts ) this._touchMap = {} this.singleDistExplorer.loadAreaTree((error, areaTree) => { if (error) throw error this.filterAreaTree(areaTree) this.singleCountryNode = areaTree this.isDistReady = true if (this.waitFnList.length) { for (let i = 0, len = this.waitFnList.length; i < len; i++) { this.waitFnList[i][0].call(this.waitFnList[i][1]) } this.waitFnList.length = 0 } // console.log('this._opts.topAdcodes: ', this._opts.topAdcodes) this.singleDistExplorer.loadMultiAreaNodes(this._opts.topAdcodes) }) } pixelToLngLat(x, y, pz) { return SphericalMercator.pointToLngLat([x, y], pz) } getBounds(node) { const nodeBounds = node.bbounds return new AMap.Bounds( this.pixelToLngLat(nodeBounds.x, nodeBounds.y + nodeBounds.height, 20), this.pixelToLngLat(nodeBounds.x + nodeBounds.width, nodeBounds.y, 20) ) } filterAreaTree(root) { const stack = [root] do { const node = stack.pop() this.nodeMap[node.adcode] = node const bbox = node.bbox node.bbounds = new BoundsItem(bbox[0], bbox[1], bbox[2], bbox[3]) node.bbox = this.getBounds(node) if (node.children) for (let children = node.children, i = 0, len = children.length; i < len; i++) { children[i].childIdx = i stack.push(children[i]) } } while (stack.length) } isReady() { return this.isDistReady } getParentAdcode(adcode, acroutes) { if (!acroutes) { const node = this.getNodeByAdcode(adcode) if (!node) { console.warn(`Can not find node: ${adcode}`) return null } acroutes = node.acroutes } return acroutes && acroutes.length ? acroutes[acroutes.length - 1] : null } getSubIdx(subAdcode) { return this.getNodeByAdcode(subAdcode).childIdx } getChildrenNum(adcode) { const node = this.getNodeByAdcode(adcode) return this.getChildrenNumOfNode(node) } getChildrenNumOfNode(node) { return node.children ? node.children.length : node.childrenNum || 0 } getNodeByAdcode(adcode) { const node = this.nodeMap[adcode] if (!node) { let areaNode = this.singleDistExplorer.getLocalAreaNode(`${`${adcode}`.substr(0, 4)}00`) areaNode || (areaNode = this.singleDistExplorer.getLocalAreaNode(`${`${adcode}`.substr(0, 2)}0000`)) if (!areaNode) return null for (let subFeatures = areaNode.getSubFeatures(), i = 0, len = subFeatures.length; i < len; i++) if (subFeatures[i].properties.adcode === adcode) return subFeatures[i].properties } return node } getNodeChildren(adcode) { const node = this.getNodeByAdcode(adcode) if (!node) return null if (node.children) return node.children if (node.childrenNum >= 0) { const areaNode = this.singleDistExplorer.getLocalAreaNode(adcode) if (!areaNode) return null const children: any[] = [], subFeatures = areaNode.getSubFeaturesInPixel() for (let i = 0, len = subFeatures.length; i < len; i++) children.push(subFeatures[i].properties) return children } return null } getExplorer() { return this.singleDistExplorer } traverseCountry(bounds, zoom, handler, finish, thisArg) { this.traverseNode(this.singleCountryNode, bounds, zoom, handler, finish, thisArg, []) } getNodeBoundsSize(node, zoom) { const pz = this.getPixelZoom(), scale = Math.pow(2, pz - zoom) return [node.bbounds.width / scale, node.bbounds.height / scale] } doesRingRingIntersect(mapBounds: AMap.Bounds, bounds: AMap.Bounds) { const mapArray = [ mapBounds.getNorthWest().toArray(), mapBounds.getNorthEast().toArray(), mapBounds.getSouthEast().toArray(), mapBounds.getSouthWest().toArray(), mapBounds.getNorthWest().toArray() ] const boxArray = [ bounds.getNorthWest().toArray(), bounds.getNorthEast().toArray(), bounds.getSouthEast().toArray(), bounds.getSouthWest().toArray(), bounds.getNorthWest().toArray() ] return !!intersect(polygon([mapArray]), polygon([boxArray])) } traverseNode(topNode, bounds: AMap.Bounds, zoom, handler, finish, thisArg, excludedAdcodes, deepCount?: DeepCount) { if (!(excludedAdcodes && excludedAdcodes.indexOf(topNode.adcode) >= 0)) { if (this.doesRingRingIntersect(bounds, topNode.bbox as AMap.Bounds)) { const children = topNode.children, hasChildren = children && children.length > 0 if (zoom > topNode.idealZoom && hasChildren) { for (let i = 0, len = children.length; i < len; i++) { this.traverseNode(children[i], bounds, zoom, handler, null, thisArg, excludedAdcodes) } } else handler.call(thisArg, topNode) } if (finish) { if (deepCount) { deepCount.count++ if (deepCount.count >= deepCount.total) { finish.call(thisArg) } } else { finish.call(thisArg) } } } } onReady(fn, thisArg, canSync?: any) { this.isDistReady ? canSync ? fn.call(thisArg) : setTimeout(function () { fn.call(thisArg) }, 0) : this.waitFnList.push([fn, thisArg]) } getPixelZoom() { return this.singleCountryNode?.pz } loadAreaNode(adcode, callback, thisArg, callSync) { this.singleDistExplorer.loadAreaNode(adcode, callback, thisArg, callSync) } isExcludedAdcode(adcode) { const excludedAdcodes = this._opts.excludedAdcodes return excludedAdcodes && excludedAdcodes.indexOf(adcode) >= 0 } traverseTopNodes(bounds: AMap.Bounds, zoom, handler, finish, thisArg) { const topAdcodes = this._opts.topAdcodes, excludedAdcodes = this._opts.excludedAdcodes, deepCount: DeepCount = { total: topAdcodes.length, count: 0 } for (let i = 0, len = topAdcodes.length; i < len; i++) { const node = this.getNodeByAdcode(topAdcodes[i]) if (!node) throw new Error(`Can not find adcode: ${topAdcodes[i]}`) this.traverseNode(node, bounds, zoom, handler, finish, thisArg, excludedAdcodes, deepCount) } } tryClearCache(tag, maxLeft) { if (!(maxLeft < 0)) { const stack = [this.singleCountryNode], list: any[] = [], touchMap = this._touchMap do { const node = stack.pop() node.children && utils.mergeArray(stack, node.children) const exTag = touchMap[node.adcode] exTag && exTag !== tag && list.push(node.adcode) } while (stack.length) list.sort(function (a, b) { const diff = touchMap[a] - touchMap[b] return 0 === diff ? a - b : diff }) const toDelLen = list.length - maxLeft if (!(toDelLen <= 0)) for (let i = 0; i < toDelLen; i++) this.singleDistExplorer.clearAreaNodeCacheByAdcode(list[i]) && this.touchAdcode(list[i], null) } } touchAdcode(adcode, tag) { this._touchMap[adcode] = tag } destroy() { this.singleDistExplorer.destroy() this._touchMap = {} this.nodeMap = {} this.singleDistExplorer = undefined as any this._opts = undefined this.waitFnList = [] this.singleCountryNode = undefined } }