| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- import * as KMap from '@/utils/ol-map/KMap.js'
- import * as util from '@/common/ol_common.js'
- import VectorLayer from 'ol/layer/Vector.js'
- import { Vector as VectorSource } from 'ol/source'
- import { newRegionFeature } from '@/utils/map.js'
- import Style from 'ol/style/Style'
- import Fill from 'ol/style/Fill'
- import Stroke from 'ol/style/Stroke'
- import Overlay from 'ol/Overlay'
- import { getCenter } from 'ol/extent'
- import { createRegionInfoCard, updateRegionInfoCard } from './regionInfoCard.js'
- const FILL_DEFAULT = 'rgba(0, 0, 0, 0.06)'
- const FILL_SELECTED = 'rgba(255, 166, 23, 0.25)'
- const STROKE_DEFAULT = '#ffffff'
- const STROKE_SELECTED = '#FFA617'
- /**
- * 地图分区图层 + 分区信息框
- */
- class RegionLayer {
- constructor(kmap, options = {}) {
- const that = this
- this.kmap = kmap
- this.enterSelectTree = !!options.enterSelectTree
- this.onRegionSelect = options.onRegionSelect
- this.selectedRegionId = null
- this.regionOverlays = []
- this._regionClickHandler = null
- this._selectedEntry = null
- this._styleCache = {
- normal: new Style({
- fill: new Fill({ color: FILL_DEFAULT }),
- stroke: new Stroke({ color: STROKE_DEFAULT, width: 1 }),
- }),
- selected: new Style({
- fill: new Fill({ color: FILL_SELECTED }),
- stroke: new Stroke({ color: STROKE_SELECTED, width: 2 }),
- }),
- }
- this.regionLayer = new KMap.VectorLayer('regionLayer', 99, {
- minZoom: 14.6,
- renderMode: 'image',
- updateWhileAnimating: false,
- updateWhileInteracting: false,
- style: (f) => that.getRegionStyle(f),
- })
- kmap.addLayer(this.regionLayer.layer)
- }
- get map() {
- return this.kmap.map
- }
- getRegionStyle(feature) {
- return feature.get('selected')
- ? [this._styleCache.selected]
- : [this._styleCache.normal]
- }
- getFeatureCenter(feature, item) {
- if (item?.pointWkt) {
- try {
- return util.wktCastGeom(item.pointWkt).getFirstCoordinate()
- } catch {
- /* ignore */
- }
- }
- const geom = feature.getGeometry()
- if (!geom) return null
- const type = geom.getType()
- if (type === 'Polygon' && typeof geom.getInteriorPoint === 'function') {
- return geom.getInteriorPoint().getCoordinates()
- }
- if (type === 'MultiPolygon' && typeof geom.getInteriorPoints === 'function') {
- const points = geom.getInteriorPoints()
- return points.getPoint(0).getCoordinates()
- }
- return getCenter(geom.getExtent())
- }
- clearRegionInfoOverlays() {
- this.regionOverlays.forEach(({ overlay }) => {
- this.map.removeOverlay(overlay)
- })
- this.regionOverlays = []
- }
- clearRegionClick() {
- if (this._regionClickHandler) {
- this.kmap.off('singleclick', this._regionClickHandler)
- this._regionClickHandler = null
- }
- }
- bindRegionClick() {
- this.clearRegionClick()
- const that = this
- this._regionClickHandler = (evt) => {
- if (!that.enterSelectTree) return
- let hit = null
- that.map.forEachFeatureAtPixel(
- evt.pixel,
- (feature, layer) => {
- if (layer !== that.regionLayer.layer) return
- const entry = that.regionOverlays.find((r) => r.feature === feature)
- if (entry) hit = entry
- },
- {
- layerFilter: (layer) => layer === that.regionLayer.layer,
- hitTolerance: 5,
- },
- )
- if (hit) that.selectRegion(hit)
- }
- this.kmap.on('singleclick', this._regionClickHandler)
- }
- selectRegion(entry) {
- if (!this.enterSelectTree || !entry) return
- if (this._selectedEntry === entry) return
- const prev = this._selectedEntry
- if (prev) {
- prev.feature.set('selected', false)
- updateRegionInfoCard(prev.el, {
- enterSelectTree: true,
- selected: false,
- })
- }
- entry.feature.set('selected', true)
- this._selectedEntry = entry
- this.selectedRegionId = entry.item.id ?? entry.item.regionId ?? entry.feature.getId()
- updateRegionInfoCard(entry.el, {
- enterSelectTree: true,
- selected: true,
- })
- // 合并为一次图层刷新,减轻 Canvas 矢量层整层重绘闪烁
- const source = this.regionLayer.layer.getSource()
- if (source) {
- source.changed()
- }
- if (typeof this.onRegionSelect === 'function') {
- this.onRegionSelect(entry.item, entry.feature)
- }
- }
- addRegionInfoOverlay(item, feature) {
- const coord = this.getFeatureCenter(feature, item)
- if (!coord) return null
- const entry = { item, feature, el: null, overlay: null }
- const el = createRegionInfoCard(item, {
- enterSelectTree: this.enterSelectTree,
- selected: false,
- onClick: () => this.selectRegion(entry),
- })
- entry.el = el
- const overlay = new Overlay({
- element: el,
- position: coord,
- positioning: 'bottom-center',
- stopEvent: this.enterSelectTree,
- offset: [0, -4],
- })
- entry.overlay = overlay
- this.map.addOverlay(overlay)
- if (this.enterSelectTree) this.bindCardClick(entry)
- return entry
- }
- bindCardClick(entry) {
- if (!entry.el || entry.el._regionSelectBound) return
- entry.el.addEventListener('click', (e) => {
- e.stopPropagation()
- if (this.enterSelectTree) this.selectRegion(entry)
- })
- entry.el._regionSelectBound = true
- }
- setEnterSelectTree(val) {
- this.enterSelectTree = !!val
- this.regionOverlays.forEach((r) => {
- updateRegionInfoCard(r.el, {
- enterSelectTree: this.enterSelectTree,
- selected: !!r.feature.get('selected'),
- })
- if (r.el) {
- r.el.style.pointerEvents = this.enterSelectTree ? 'auto' : 'none'
- if (this.enterSelectTree) this.bindCardClick(r)
- }
- if (r.overlay?.setStopEvent) {
- r.overlay.setStopEvent(this.enterSelectTree)
- }
- })
- if (this.enterSelectTree) {
- this.bindRegionClick()
- } else {
- this.clearRegionClick()
- this.regionOverlays.forEach((r) => {
- r.feature.set('selected', false)
- updateRegionInfoCard(r.el, { enterSelectTree: false, selected: false })
- })
- this.selectedRegionId = null
- this._selectedEntry = null
- this.regionLayer.layer.getSource()?.changed()
- }
- }
- initData(data) {
- this.clearLayer()
- if (!data || !data.length) return
- const features = []
- for (const item of data) {
- item.regionWkt = item.regionWkt || item.wkt
- if (!item.regionWkt) {
- console.warn('[regionLayer] 分区缺少 wkt', item)
- continue
- }
- try {
- const f = newRegionFeature({
- ...item,
- bgName: 'defalut',
- bgColor: 'defalut',
- selected: false,
- })
- features.push(f)
- const entry = this.addRegionInfoOverlay(item, f)
- if (entry) {
- this.regionOverlays.push(entry)
- }
- } catch (err) {
- console.warn('[regionLayer] 解析分区失败', item, err)
- }
- }
- this.area = features
- const source = new VectorSource({ features })
- this.regionLayer.layer.setSource(source)
- if (features.length) {
- const extent = source.getExtent()
- if (extent.every((n) => Number.isFinite(n))) {
- this.kmap.getView().fit(extent, {
- padding: [60, 40, 100, 40],
- duration: 600,
- maxZoom: 19,
- })
- }
- }
- if (this.enterSelectTree) {
- this.bindRegionClick()
- }
- }
- clearLayer() {
- this.clearRegionClick()
- this.clearRegionInfoOverlays()
- this.selectedRegionId = null
- this._selectedEntry = null
- if (this.regionLayer?.layer?.getSource()) {
- this.regionLayer.layer.getSource().clear()
- }
- }
- reset(farm) {
- this.clearLayer()
- this.initData(farm?.id ? [] : [])
- }
- }
- export default RegionLayer
|