mapConfig.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import { LineLayer, PointLayer, PolygonLayer, Popup, RasterLayer, Scene } from '@antv/l7'
  2. import type { ICameraOptions, ISourceCFG, Point } from '@antv/l7-core'
  3. import { Mapbox } from '@antv/l7-maps'
  4. import { formatStringByTemplate } from '@/utils/string'
  5. import bus from '@/utils/bus'
  6. import boundary from '@/assets/json/zmw.json'
  7. import boundaryTri from '@/assets/json/tri.json'
  8. import thData from '@/assets/json/thly-all.json'
  9. import lakeData from '@/assets/json/lake.json'
  10. import thly from '@/assets/images/thly-all.png'
  11. export let scene: Scene
  12. // 加载状态
  13. export let loadedStatus = false
  14. export let imageMap = new Map()
  15. function loadImage(images: any[]) {
  16. images.forEach((item: any) => {
  17. if (!imageMap.has(item.name)) {
  18. imageMap.set(item.name, item.src)
  19. scene.addImage(item.name, item.src)
  20. }
  21. })
  22. }
  23. /**
  24. * 初始化地图
  25. */
  26. export function init(config: any) {
  27. loadedStatus = false
  28. imageMap = new Map()
  29. scene = new Scene({
  30. id: 'mapDiv',
  31. map: new Mapbox({
  32. center: [120.83, 31.05],
  33. zoom: 9,
  34. maxZoom: 10,
  35. minZoom: 5,
  36. pitch: 90, // 地图倾斜度
  37. style: 'blank'
  38. })
  39. })
  40. scene.on('loaded', () => {
  41. loadedStatus = true
  42. loadBaseLayer()
  43. // 加载图片
  44. if (config && config.images && config.images.length > 0) {
  45. loadImage(config.images)
  46. }
  47. setTimeout(function() {
  48. const map = scene.map
  49. const startZoom = 6.5
  50. const endZoom = 9
  51. const duration = 6000 // 动画持续时间,单位毫秒
  52. const interval = 100 // 每次更新间隔,单位毫秒
  53. const step = (endZoom - startZoom) * (interval / duration)
  54. const zoomAnimation = setInterval(() => {
  55. const currentZoom = map.getZoom()
  56. if (currentZoom <= startZoom) {
  57. clearInterval(zoomAnimation)
  58. map.setZoom(startZoom)
  59. } else {
  60. const newZoom = currentZoom - step
  61. map.setZoom(newZoom)
  62. }
  63. }, interval)
  64. }, 9000)
  65. })
  66. // 地图平移时触发事件
  67. scene.on('mapmove', () => {
  68. console.log('中心点:', scene.getCenter().lng, scene.getCenter().lat, scene.getZoom(), scene.getPitch())
  69. })
  70. }
  71. /**
  72. * 加载底图
  73. */
  74. function loadBaseLayer() {
  75. // //添加太湖流域地图
  76. const thMap = new PolygonLayer({})
  77. .source(thData) //使用的数据为下载到本地的json数据
  78. .shape('extrude') //用于绘制几何体
  79. .size(30000)
  80. .active({
  81. color: 'rgba(0, 0, 0, .1)'
  82. })
  83. .style({
  84. mapTexture: thly,
  85. heightfixed: true, //抬升高度是否随 zoom 变化
  86. raisingHeight: 1000, //抬升高度
  87. sourceColor: '#67bdec', //抬高高度的颜色
  88. targetColor: '#67bdec',
  89. opacity: 0.5
  90. })
  91. const triLayer = new LineLayer({ zIndex: 2 })
  92. .source(boundaryTri)
  93. .color('rgb(241,152,49)')
  94. .size(2)
  95. .style({
  96. raisingHeight: 50000
  97. })
  98. scene.addLayer(triLayer)
  99. const layerBoundary = new LineLayer({ zIndex: 2 })
  100. .source(boundary)
  101. .size(50)
  102. .shape('wall')
  103. .style({
  104. opacity: 1,
  105. sourceColor: '#0DCCFF',
  106. targetColor: 'rbga(255,255,255, 0)',
  107. raisingHeight: 50000
  108. })
  109. scene.addLayer(layerBoundary)
  110. scene.addLayer(thMap)
  111. setMark(lakeData)
  112. }
  113. /**
  114. * 设置地图视角
  115. * @param center 中心点
  116. * @param zoom 层级
  117. * @param pitch 倾斜角度
  118. * @param padding
  119. */
  120. export function setCenter(center: Point, zoom: number, pitch = 0, padding = {}) {
  121. if (center && zoom) {
  122. scene.setZoomAndCenter(zoom, center)
  123. } else if (center) {
  124. scene.setCenter(center, padding as ICameraOptions)
  125. } else if (zoom) {
  126. scene.setZoom(zoom)
  127. }
  128. scene.setPitch(pitch)
  129. }
  130. export function loadMap(option: any) {
  131. const loadMapFun = (option: any) => {
  132. if (option.view) {
  133. setCenter(option.view.center, option.view.zoom)
  134. }
  135. if (option.layers) {
  136. // 清空图层
  137. scene.removeAllLayer()
  138. // 加载地图
  139. loadBaseLayer()
  140. option.layers.forEach((layer: any) => {
  141. switch (layer.type) {
  142. case 'point':
  143. createPointByCongfig(layer)
  144. break
  145. // case 'line':
  146. // createLineByCongfig(layer, mapJson)
  147. // break
  148. // case 'polygon':
  149. // createPolygonByCongfig(layer, mapJson)
  150. // break
  151. // case 'mark':
  152. // setMark(config)
  153. // break
  154. }
  155. })
  156. }
  157. }
  158. function checkStatus(option: any) {
  159. if (loadedStatus) {
  160. loadMapFun(option)
  161. } else {
  162. setTimeout(() => checkStatus(option), 1000)
  163. }
  164. }
  165. checkStatus(option)
  166. }
  167. function createPointByCongfig(config: any) {
  168. console.log('config', config, imageMap)
  169. const layer = new PointLayer({})
  170. .source(config.data, config.dataOptions)
  171. .size(config.size)
  172. .active(config.emphasis.show).shape('triangleColumn')
  173. .color('#3ae33d')
  174. // setLayerShape(layer, config.shape)
  175. // 颜色配置
  176. // setLayerColor(layer, config.color);
  177. scene.addLayer(layer)
  178. setMark(config)
  179. // setPopup(layer, config);
  180. // 点击事件
  181. layer.on('click', (e: any) => {
  182. let row = e.feature.properties || e.feature
  183. bus.emit('point_click', { data: row, e })
  184. })
  185. }
  186. function createLakePoint(config: any) {
  187. const layer = new PointLayer({})
  188. .source(config.data, config.dataOptions)
  189. .size(config.size)
  190. .active(config.emphasis.show)
  191. setLayerShape(layer, config.shape)
  192. // 颜色配置
  193. // setLayerColor(layer, config.color);
  194. scene.addLayer(layer)
  195. setMark(config)
  196. // setPopup(layer, config);
  197. // 点击事件
  198. layer.on('click', (e: any) => {
  199. let row = e.feature.properties || e.feature
  200. bus.emit('point_click', { data: row, e })
  201. })
  202. }
  203. function getValueByConditions(data: any, conditions: any, field: any) {
  204. let condition
  205. for (const item of conditions) {
  206. if (compareByConditions(data, item)) {
  207. condition = item
  208. break
  209. }
  210. }
  211. if (!condition) {
  212. return null
  213. }
  214. if (field === 'color') {
  215. return condition[field]
  216. }
  217. if (condition[field] !== 'image') {
  218. return condition[field]
  219. }
  220. if (condition[field] === 'image') {
  221. return condition.imageName
  222. }
  223. return null
  224. }
  225. function compareByConditions(data: any, condition: any) {
  226. switch (condition.cond) {
  227. case 'lt':
  228. if (data < condition.value) {
  229. return true
  230. }
  231. break
  232. case 'lte':
  233. if (data <= condition.value) {
  234. return true
  235. }
  236. break
  237. case 'eq':
  238. if (data == condition.value) {
  239. return true
  240. }
  241. break
  242. case 'gt':
  243. if (data > condition.value) {
  244. return true
  245. }
  246. break
  247. case 'gte':
  248. if (data >= condition.value) {
  249. return true
  250. }
  251. break
  252. case 'ne':
  253. if (data != condition.value) {
  254. return true
  255. }
  256. break
  257. }
  258. return false
  259. }
  260. /**
  261. * 图层数据配置
  262. * @param dataType
  263. * @returns {{parser: {x: string, y: string, type: string}}|null}
  264. */
  265. function getSourceOptions(dataType: any) {
  266. switch (dataType) {
  267. case 'geojson':
  268. return null
  269. case 'list':
  270. return {
  271. parser: {
  272. type: 'json',
  273. x: '经度',
  274. y: '纬度'
  275. }
  276. }
  277. default:
  278. }
  279. }
  280. /**
  281. * 图层形状配置
  282. * @param layer
  283. * @param config
  284. */
  285. function setLayerShape(layer: any, config: any) {
  286. if (config.isConstant) {
  287. layer.shape(config.value)
  288. } else {
  289. layer.shape(config.field, config.value)
  290. }
  291. }
  292. /**
  293. * 图层颜色配置
  294. * @param layer
  295. * @param config
  296. */
  297. function setLayerColor(layer: any, config: any) {
  298. if (config.isConstant) {
  299. layer.color(config.value)
  300. } else {
  301. const field = config.field
  302. const conditions = config.conditions
  303. layer.color(field, (field: any) => {
  304. return getValueByConditions(field, conditions, 'color')
  305. })
  306. }
  307. }
  308. export function setMark(config: any) {
  309. if (!config.label.show) {
  310. return
  311. }
  312. // 标注
  313. const markLayer = new PointLayer({})
  314. .source(config.data, config.dataOptions)
  315. .shape(config.label.field, 'text')
  316. .size(16)
  317. .color(config.label.color || '#000')
  318. .style({
  319. textAnchor: config.label.position || 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
  320. textOffset: [0, 40], // 文本相对锚点的偏移量 [水平, 垂直]
  321. spacing: 2, // 字符间距
  322. padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
  323. stroke: config.label.stroke || '#ffffff', // 描边颜色
  324. strokeWidth: 2, // 描边宽度
  325. strokeOpacity: 1.0
  326. })
  327. scene.addLayer(markLayer)
  328. }
  329. function setPopup(layer: any, config: any) {
  330. if (!config.enabled) {
  331. return
  332. }
  333. layer.on(config.trigger, (e: any) => {
  334. let properties = e.feature.properties || e.feature
  335. const content = formatStringByTemplate(properties, config.content)
  336. // offsets: [0, 0], closeButton: false,
  337. const popup = new Popup({})
  338. .setLnglat(e.lngLat)
  339. .setHTML(content)
  340. scene.addPopup(popup)
  341. })
  342. }
  343. /**
  344. * 创建线图层
  345. * @param config 配置
  346. * @param dataType 图层数据类型
  347. * @param mapData 图层数据
  348. */
  349. function createLineByCongfig(config: any, dataType: any, mapData: any) {
  350. const sourceOptions = getSourceOptions(dataType) as ISourceCFG
  351. const layer = new LineLayer({})
  352. .source(mapData, sourceOptions)
  353. .size(1)
  354. .style({
  355. lineType: 'dash',
  356. dashArray: [2, 2]
  357. })
  358. .active(config.active)
  359. scene.addLayer(layer)
  360. setMark(config)
  361. setPopup(layer, config.popup)
  362. }
  363. /**
  364. * 创建面图层
  365. * @param config 配置
  366. * @param dataType 图层数据类型
  367. * @param mapData 图层数据
  368. */
  369. function createPolygonByCongfig(config: any, dataType: any, mapData: any) {
  370. const sourceOptions = getSourceOptions(dataType) as ISourceCFG
  371. const layer = new PolygonLayer({})
  372. .source(mapData, sourceOptions)
  373. .shape('fill')
  374. .active(config.active)
  375. // 颜色配置
  376. setLayerColor(layer, config.color)
  377. scene.addLayer(layer)
  378. setMark(config)
  379. setPopup(layer, config.popup)
  380. }
  381. export function getLegendData(config: any) {
  382. const layers = config.layers
  383. const layerLegend = layers.reduce((acc: any, item: any) => {
  384. if (item.legend && item.legend.data) {
  385. return acc.concat(item.legend.data)
  386. } else {
  387. return acc
  388. }
  389. }, [])
  390. // return layerLegend.map((item: any) => {
  391. // const data = {label: item.text}
  392. // if (item.shape === 'image') {
  393. // data.imgType = 'img'
  394. // data.src = item.imgSrc
  395. // } else {
  396. // data.imgType = 'svg'
  397. // data.src = item.shape
  398. // data.imgStyle = {fill: item.color}
  399. // }
  400. // return data
  401. // })
  402. return null
  403. }