clusterPointsLayer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import Style from 'ol/style/Style'
  2. import Photo from 'ol-ext/style/Photo'
  3. import { newPoint } from '@/utils/map.js'
  4. import VectorLayer from 'ol/layer/Vector.js'
  5. import { Cluster, Vector as VectorSource } from 'ol/source.js'
  6. import { Vector } from 'ol/layer.js'
  7. import * as KMap from '@/utils/ol-map/KMap.js'
  8. import { Fill, Text, Icon, Stroke } from 'ol/style.js'
  9. // import eventBus from '@/api/eventBus.js'
  10. import { extractCoordinates } from '@/common/commonFun.js'
  11. import treeImg from '@/assets/img/map/tree.png'
  12. import treeActiveImg from '@/assets/img/map/tree-active.png'
  13. import activeBgImg from '@/assets/img/map/active-bg.png'
  14. import hereImg from '@/assets/img/map/here.png'
  15. import progressImg from '@/assets/img/map/progress.png'
  16. import progressActiveImg from '@/assets/img/map/progress-active.png'
  17. /**
  18. * 果树聚合点位图层
  19. */
  20. class ClusterPointsLayer {
  21. constructor(kmap) {
  22. const that = this
  23. this.cloudFilenameCache = {}
  24. this.statusTitleStyleCache = {}
  25. this.textBgStyleCache = {}
  26. this.bgStyleCache = {}
  27. this.activeBgStyleCache = {}
  28. this.progressBgStyleCache = {}
  29. this.enterSelectTree = false
  30. this.selectableActiveTree = null
  31. that.initLayer(kmap)
  32. that.kmap = kmap
  33. // that.addMapSingerClick()
  34. this.currentPoint = null
  35. }
  36. addMapSingerClick() {
  37. const that = this
  38. that.kmap.on('singleclick', (evt) => {
  39. that.kmap.map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
  40. if (layer instanceof VectorLayer && layer.get('name') === 'clusterPointsLayer') {
  41. const fs = that.selectFeature(feature)
  42. // if (that.selectableActiveTree) {
  43. // that.selectableActiveTree.set('selectableActive', false)
  44. // }
  45. // if (that.enterSelectTree) {
  46. // // eventBus.emit('showTreePopup', fs.get('sampleId'))
  47. // that.selectableActiveTree = fs
  48. // fs.set('selectableActive', true)
  49. // return
  50. // }
  51. // const miniUserId = fs.get('miniUserId') && !fs.get('virtual')
  52. // const isMy = fs.get('my')
  53. // if (isMy) {
  54. // localStorage.setItem('showMapText', true)
  55. // } else if (miniUserId) {
  56. // // eventBus.emit('adoptMap:friendTree', {
  57. // // sampleId: fs.get('sampleId'),
  58. // // })
  59. // } else {
  60. // // eventBus.emit('showTreePopup', fs.get('sampleId'))
  61. // that.selectableActiveTree = fs
  62. // fs.set('selectableActive', true)
  63. // }
  64. }
  65. })
  66. })
  67. }
  68. initLayer(kmap) {
  69. const that = this
  70. this.vectorStyle = new KMap.VectorStyle()
  71. this.clusterSource = new VectorSource({})
  72. this.clusterLayer = new Vector({
  73. source: new Cluster({
  74. distance: 40,
  75. source: this.clusterSource,
  76. }),
  77. className: 'ol-layer-cluster',
  78. name: 'clusterPointsLayer',
  79. minZoom: 14.6,
  80. maxZoom: 22,
  81. zIndex: 1001,
  82. style: function (feature) {
  83. const f = that.selectFeature(feature)
  84. const isVisible = f.get('visible') !== false
  85. const selectableActive = f.get('selectableActive')
  86. if (!isVisible) {
  87. return []
  88. }
  89. if (that.enterSelectTree || selectableActive) {
  90. return new Style({
  91. image: new Icon({
  92. src: selectableActive ? treeActiveImg : treeImg,
  93. scale: 0.55,
  94. }),
  95. })
  96. }
  97. const isOther = f.get('miniUserId') ? true : false
  98. const isHigh = f.get('my')
  99. if (!isHigh && !isOther) {
  100. return new Style({
  101. image: new Icon({
  102. src: treeImg,
  103. scale: 0.55,
  104. }),
  105. })
  106. }
  107. const img = f.get('icon')
  108. const count = 30
  109. const startColor = isHigh ? '#FFD887' : '#FFFFFFb3'
  110. const endColor = isHigh ? '#ED9E1E' : '#FFFFFFb3'
  111. const styles = [
  112. that.pointStyle(img, isHigh),
  113. that.progressStyle(count, isHigh),
  114. that.textBgStyle(count, startColor, endColor, isHigh),
  115. ]
  116. if (isHigh) {
  117. styles.push(
  118. that.activePointBgStyle(),
  119. that.statusTitleStyle(
  120. f.get('treeName'),
  121. 0,
  122. -36,
  123. '#FFFFFF',
  124. '#B38A00',
  125. 14,
  126. isHigh,
  127. ),
  128. )
  129. if (!localStorage.getItem('showMapText')) {
  130. styles.push(that.activeTextStyle())
  131. }
  132. }
  133. return styles
  134. },
  135. })
  136. kmap.addLayer(this.clusterLayer)
  137. this.kmap = kmap
  138. }
  139. setVisible(visible) {
  140. if (this.clusterLayer) {
  141. this.clusterLayer.setVisible(visible)
  142. }
  143. }
  144. setData(data, enterSelectTreeVal) {
  145. const that = this
  146. this.enterSelectTree = enterSelectTreeVal
  147. if (enterSelectTreeVal) {
  148. this.setVisible(false)
  149. this.clusterSource.clear()
  150. return
  151. }
  152. this.setVisible(true)
  153. this.clusterSource.clear()
  154. const features = []
  155. if (!data || !data.length) return
  156. const myPoint = data.find((item) => item.my)
  157. for (const item of data) {
  158. try {
  159. const f = newPoint(item, 'geom', 'cluster-point')
  160. features.push(f)
  161. } catch (err) {
  162. console.log('err', err)
  163. }
  164. }
  165. this.clusterSource.addFeatures(features)
  166. this.kmap.getView().fit(this.clusterSource.getExtent(), {
  167. duration: 1000,
  168. padding: [0, 10, 0, 10],
  169. })
  170. if (myPoint) {
  171. setTimeout(() => {
  172. const pointPosition = extractCoordinates(myPoint.geom)
  173. const view = this.kmap.getView()
  174. view.animate({
  175. duration: 1200,
  176. zoom: 20,
  177. center: pointPosition,
  178. })
  179. }, 1100)
  180. }
  181. }
  182. textBgStyle(count, startColor, endColor, isHigh) {
  183. const key = count + startColor + endColor + isHigh
  184. let style = this.textBgStyleCache[key]
  185. let length = 5
  186. let offsetX = 0
  187. let highOffsetX = 0
  188. if (count === 0) {
  189. length = 0
  190. } else if (count < 7) {
  191. length = 10
  192. offsetX = 14
  193. highOffsetX = 14
  194. } else if (count > 7 && count <= 14) {
  195. length = 20
  196. offsetX = 6
  197. highOffsetX = 7
  198. } else if (count > 14 && count <= 21) {
  199. length = 28
  200. offsetX = 4
  201. highOffsetX = 5
  202. } else if (count > 21 && count <= 29) {
  203. length = 34
  204. offsetX = 2
  205. highOffsetX = 3
  206. } else if (count === 30) {
  207. length = 38
  208. offsetX = 0
  209. highOffsetX = 0
  210. }
  211. if (!style) {
  212. style = new Style({
  213. renderer: function (coordinates, state) {
  214. const ctx = state.context
  215. const x =
  216. coordinates[0] -
  217. (isHigh ? highOffsetX * state.pixelRatio : offsetX * state.pixelRatio)
  218. const y =
  219. coordinates[1] + (isHigh ? 24 * state.pixelRatio : 17 * state.pixelRatio)
  220. const width = isHigh
  221. ? (length + 10) * state.pixelRatio
  222. : length * state.pixelRatio
  223. const height = isHigh ? 10 * state.pixelRatio : 6 * state.pixelRatio
  224. const cornerRadius = isHigh ? 5 * state.pixelRatio : 3 * state.pixelRatio
  225. const gradient = ctx.createLinearGradient(x - width / 2, y, x + width / 2, y)
  226. gradient.addColorStop(0, startColor)
  227. gradient.addColorStop(1, endColor)
  228. ctx.beginPath()
  229. ctx.moveTo(x - width / 2 + cornerRadius, y - height / 2)
  230. ctx.lineTo(x + width / 2 - cornerRadius, y - height / 2)
  231. ctx.arc(
  232. x + width / 2 - cornerRadius,
  233. y - height / 2 + cornerRadius,
  234. cornerRadius,
  235. -Math.PI / 2,
  236. 0,
  237. )
  238. ctx.lineTo(x + width / 2, y + height / 2 - cornerRadius)
  239. ctx.arc(
  240. x + width / 2 - cornerRadius,
  241. y + height / 2 - cornerRadius,
  242. cornerRadius,
  243. 0,
  244. Math.PI / 2,
  245. )
  246. ctx.lineTo(x - width / 2 + cornerRadius, y + height / 2)
  247. ctx.arc(
  248. x - width / 2 + cornerRadius,
  249. y + height / 2 - cornerRadius,
  250. cornerRadius,
  251. Math.PI / 2,
  252. Math.PI,
  253. )
  254. ctx.lineTo(x - width / 2, y - height / 2 + cornerRadius)
  255. ctx.arc(
  256. x - width / 2 + cornerRadius,
  257. y - height / 2 + cornerRadius,
  258. cornerRadius,
  259. Math.PI,
  260. -Math.PI / 2,
  261. )
  262. ctx.closePath()
  263. ctx.fillStyle = gradient
  264. ctx.fill()
  265. },
  266. zIndex: isHigh ? 12 : 10,
  267. })
  268. this.textBgStyleCache[key] = style
  269. }
  270. return style
  271. }
  272. statusTitleStyle(statusName, offsetX, offsetY, color, strokeColor, fontSize, isHigh) {
  273. const key = statusName + '-' + offsetY
  274. let style = this.statusTitleStyleCache[key]
  275. if (!style) {
  276. style = new Style({
  277. text: new Text({
  278. text: statusName,
  279. offsetX: offsetX,
  280. offsetY: offsetY,
  281. font: isHigh ? 'bold 14px sans-serif' : '12px sans-serif',
  282. fill: new Fill({ color }),
  283. stroke: new Stroke({ color: strokeColor }),
  284. }),
  285. zIndex: 13,
  286. })
  287. this.statusTitleStyleCache[key] = style
  288. }
  289. return style
  290. }
  291. pointStyle(img, isHigh) {
  292. const key = img + isHigh
  293. let bgStyle = this.bgStyleCache[key]
  294. if (!bgStyle) {
  295. bgStyle = new Style({
  296. image: new Photo({
  297. src: img,
  298. kind: 'circle',
  299. radius: isHigh ? 23 : 17,
  300. shadow: 0,
  301. crop: false,
  302. displacement: [0, 0],
  303. stroke: new Stroke({
  304. width: isHigh ? 0 : 1,
  305. color: isHigh ? '#fff' : '#C7C7C7',
  306. }),
  307. }),
  308. zIndex: isHigh ? 11 : 0,
  309. })
  310. this.bgStyleCache[key] = bgStyle
  311. }
  312. return bgStyle
  313. }
  314. activePointBgStyle() {
  315. return new Style({
  316. image: new Icon({
  317. src: activeBgImg,
  318. scale: 0.55,
  319. }),
  320. zIndex: 10,
  321. })
  322. }
  323. activeTextStyle() {
  324. return new Style({
  325. image: new Icon({
  326. src: hereImg,
  327. scale: 0.5,
  328. displacement: [0, 108],
  329. }),
  330. zIndex: 15,
  331. })
  332. }
  333. progressStyle(count, isHigh) {
  334. const key = count + isHigh
  335. let progressBgStyle = this.progressBgStyleCache[key]
  336. if (!progressBgStyle) {
  337. progressBgStyle = new Style({
  338. image: new Icon({
  339. src: isHigh ? progressActiveImg : progressImg,
  340. scale: 0.5,
  341. displacement: isHigh ? [0, -48] : [0, -34],
  342. }),
  343. zIndex: isHigh ? 12 : 0,
  344. })
  345. this.progressBgStyleCache[key] = progressBgStyle
  346. }
  347. return progressBgStyle
  348. }
  349. selectFeature(feature) {
  350. const fs = feature.get('features')
  351. if (!fs || fs.length === 0) {
  352. return feature
  353. }
  354. if (fs.length === 1) {
  355. return fs[0]
  356. }
  357. for (const item of fs) {
  358. if (item.get('my')) {
  359. return item
  360. }
  361. }
  362. for (const item of fs) {
  363. if (item.get('miniUserId')) {
  364. return item
  365. }
  366. }
  367. return fs[0]
  368. }
  369. }
  370. export default ClusterPointsLayer