@3clear/basegis 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,972 @@
1
+ # @3clear/basegis
2
+
3
+ `@3clear/basegis` 是 3clear 一张图项目抽出的 GIS 能力包。它提供一个统一入口 `BaseGIS`,把 Cesium / Leaflet 的差异收敛到适配层里;业务页面优先只操作 `BaseGIS`,复杂图层能力通过 `methods` 控制器组合 `BaseGIS` 公共方法完成。
4
+
5
+ 当前包包含:
6
+
7
+ - `BaseGIS`:地图初始化、引擎切换、视角控制、底图切换、绘制、图层显隐、DEM、点击事件等基础能力。
8
+ - `methods`:图片图层、网格图层、海量点、点位抽稀、等值线、等压线、风场、图形组等高级控制器。
9
+ - `layers`:天地图、GeoServer 金字塔瓦片、WMS、WMTS 图层配置快捷构造器。
10
+ - `assets`:GIS 示例资源。
11
+ - `style.css`:Leaflet、GIS 图层和标注相关基础样式。
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install @3clear/basegis leaflet axios pixi.js leaflet-pixi-overlay
17
+ ```
18
+
19
+ 使用样式:
20
+
21
+ ```js
22
+ import '@3clear/basegis/style.css'
23
+ ```
24
+
25
+ Cesium 当前不随 npm 包发布,宿主项目需要按原项目方式把 Cesium 静态资源放到 `public/lib/Cesium`,并保证初始化前能访问 `window.Cesium`。默认配置会从 `${BASE_URL}lib/Cesium/Cesium.js` 和 `${BASE_URL}lib/Cesium/Widgets/widgets.css` 加载。
26
+
27
+ 如果需要加载 GeoTIFF 网格,需要宿主项目提前提供 `window.GeoTIFF`。
28
+
29
+ ## 出口
30
+
31
+ ```js
32
+ // 主入口
33
+ import { BaseGIS } from '@3clear/basegis'
34
+
35
+ // 高级能力控制器
36
+ import {
37
+ GraphicGroupController,
38
+ ImageLayerController,
39
+ GridLayerController,
40
+ PointLargeLayerController,
41
+ PointDensityController,
42
+ ContourLayerController,
43
+ PressureContourLayer,
44
+ createPressureDataResolver,
45
+ buildPressureRenderData,
46
+ WindFieldMethods,
47
+ } from '@3clear/basegis/methods'
48
+
49
+ // 图层配置构造器
50
+ import {
51
+ createTiandituLayer,
52
+ createGeoserverPyramidLayer,
53
+ createWmsLayer,
54
+ createWmtsLayer,
55
+ } from '@3clear/basegis/layers'
56
+
57
+ // 示例资源
58
+ import { gisMarkerSample } from '@3clear/basegis/assets'
59
+ ```
60
+
61
+ ## 快速开始
62
+
63
+ ```vue
64
+ <template>
65
+ <div id="map" class="map"></div>
66
+ </template>
67
+
68
+ <script setup>
69
+ import { onBeforeUnmount, onMounted } from 'vue'
70
+ import { BaseGIS } from '@3clear/basegis'
71
+ import '@3clear/basegis/style.css'
72
+
73
+ let mapCore = null
74
+
75
+ onMounted(() => {
76
+ mapCore = new BaseGIS({
77
+ // 可选 cesium / leaflet。不传时使用默认配置 active: 'cesium'。
78
+ engineType: 'cesium',
79
+ // 也可以传 container: HTMLElement。
80
+ containerId: 'map',
81
+ config: {
82
+ view: {
83
+ // 可选 2d / 2.5d / 3d。
84
+ defaultSceneMode: '3d',
85
+ initialView: {
86
+ center: [104, 35],
87
+ // Cesium 使用 height,Leaflet 使用 zoom。
88
+ height: 5000000,
89
+ zoom: 5,
90
+ // Cesium 默认保持垂直俯视。
91
+ pitch: -90,
92
+ },
93
+ },
94
+ },
95
+ })
96
+
97
+ const result = mapCore.init()
98
+ if (!result.success) {
99
+ console.warn(result.message)
100
+ }
101
+ })
102
+
103
+ onBeforeUnmount(() => {
104
+ mapCore?.destroy()
105
+ })
106
+ </script>
107
+
108
+ <style scoped>
109
+ .map {
110
+ width: 100%;
111
+ height: 100%;
112
+ }
113
+ </style>
114
+ ```
115
+
116
+ ## 返回值约定
117
+
118
+ 多数方法返回统一结果对象:
119
+
120
+ ```js
121
+ {
122
+ success: true,
123
+ message: 'operation message',
124
+ data: {}
125
+ }
126
+ ```
127
+
128
+ 失败时:
129
+
130
+ ```js
131
+ {
132
+ success: false,
133
+ message: 'error message',
134
+ code: 'NOT_INITIALIZED'
135
+ }
136
+ ```
137
+
138
+ Leaflet 不支持 3D、DEM 等能力时,会返回失败结果或控制台提示,不应直接打断页面。
139
+
140
+ ## BaseGIS 配置
141
+
142
+ ```js
143
+ const mapCore = new BaseGIS({
144
+ engineType: 'cesium',
145
+ containerId: 'map',
146
+ config: {
147
+ engine: {
148
+ active: 'cesium',
149
+ cesium: {
150
+ // 本地 Cesium 静态资源地址。
151
+ scriptUrl: '/lib/Cesium/Cesium.js',
152
+ cssUrl: '/lib/Cesium/Widgets/widgets.css',
153
+ },
154
+ leaflet: {
155
+ sourceType: 'npm',
156
+ },
157
+ },
158
+ view: {
159
+ defaultSceneMode: '3d',
160
+ initialView: {
161
+ center: [121.4737, 31.2304],
162
+ height: 1800000,
163
+ zoom: 7,
164
+ heading: 0,
165
+ pitch: -90,
166
+ roll: 0,
167
+ },
168
+ },
169
+ basemap: {
170
+ defaultVisibleId: 'tianditu-imagery',
171
+ defaultAnnotationId: 'tianditu-vector-label',
172
+ list: [],
173
+ },
174
+ dem: {
175
+ defaultEnabled: true,
176
+ defaultVisibleId: 'ellipsoid-flat',
177
+ list: [],
178
+ },
179
+ },
180
+ })
181
+ ```
182
+
183
+ 说明:
184
+
185
+ - `containerId` 和 `container` 二选一即可;`init()` 时也可以再次传入。
186
+ - 页面传入的 `config` 会覆盖内置默认配置。
187
+ - Cesium 默认视角 `pitch: -90`,表示垂直俯视。
188
+ - `BaseGIS` 只保存运行时配置,调用 `setConfig()` 不会自动重建地图,需要重新 `init()` 才会创建新地图实例。
189
+
190
+ ## BaseGIS 基础能力
191
+
192
+ ### 生命周期与实例
193
+
194
+ ```js
195
+ mapCore.init({ containerId: 'map', engineType: 'leaflet' })
196
+ mapCore.destroy()
197
+
198
+ mapCore.setEngine('cesium')
199
+ mapCore.setConfig({ view: { defaultSceneMode: '2d' } })
200
+
201
+ mapCore.getEngineType()
202
+ mapCore.getConfig()
203
+ mapCore.getMapInstance()
204
+ ```
205
+
206
+ ### 视角与场景
207
+
208
+ ```js
209
+ mapCore.zoomIn()
210
+ mapCore.zoomOut()
211
+ mapCore.resetView()
212
+
213
+ mapCore.setSceneMode('2d')
214
+ mapCore.setSceneMode('2.5d')
215
+ mapCore.setSceneMode('3d')
216
+ mapCore.getSceneMode()
217
+
218
+ mapCore.setInitialView({
219
+ center: [104, 35],
220
+ height: 3000000,
221
+ zoom: 5,
222
+ pitch: -90,
223
+ })
224
+ ```
225
+
226
+ ### 底图与普通图层
227
+
228
+ ```js
229
+ mapCore.setBasemapById('tianditu-imagery')
230
+
231
+ mapCore.setBasemap({
232
+ id: 'custom-wmts',
233
+ name: '自定义 WMTS',
234
+ type: 'wmts',
235
+ provider: 'custom',
236
+ url: 'https://example.com/wmts?...',
237
+ })
238
+
239
+ mapCore.addLayer(layerConfig)
240
+ mapCore.showLayer({ layerId: 'custom-wmts' })
241
+ mapCore.hideLayer({ layerId: 'custom-wmts' })
242
+ mapCore.removeLayer({ layerId: 'custom-wmts' })
243
+ ```
244
+
245
+ ### 点线面文字
246
+
247
+ ```js
248
+ mapCore.drawPoint({
249
+ id: 'point-1',
250
+ longitude: 104,
251
+ latitude: 35,
252
+ style: {
253
+ color: '#ff4d4f',
254
+ pixelSize: 10,
255
+ },
256
+ })
257
+
258
+ mapCore.drawLine({
259
+ id: 'line-1',
260
+ positions: [
261
+ [103, 34],
262
+ [105, 36],
263
+ ],
264
+ style: {
265
+ color: '#1677ff',
266
+ width: 3,
267
+ },
268
+ })
269
+
270
+ mapCore.drawPolygon({
271
+ id: 'polygon-1',
272
+ positions: [
273
+ [102, 33],
274
+ [106, 33],
275
+ [106, 36],
276
+ [102, 36],
277
+ ],
278
+ style: {
279
+ color: 'rgba(22, 119, 255, 0.25)',
280
+ outlineColor: '#1677ff',
281
+ },
282
+ })
283
+
284
+ mapCore.drawText({
285
+ id: 'text-1',
286
+ longitude: 104,
287
+ latitude: 35,
288
+ text: '示例文字',
289
+ style: {
290
+ color: '#ffffff',
291
+ font: '14px sans-serif',
292
+ },
293
+ })
294
+
295
+ mapCore.addMarker({
296
+ id: 'marker-1',
297
+ longitude: 104,
298
+ latitude: 35,
299
+ image: '/marker.png',
300
+ width: 32,
301
+ height: 32,
302
+ })
303
+
304
+ mapCore.removeGraphic({ id: 'marker-1' })
305
+ mapCore.clearGraphics()
306
+ ```
307
+
308
+ ### 点击事件与投影
309
+
310
+ ```js
311
+ const clickResult = mapCore.onClick({
312
+ id: 'station-click',
313
+ callback(event) {
314
+ // event 中通常包含 longitude / latitude / pickedObject 等信息,具体字段与引擎有关。
315
+ console.log(event)
316
+ },
317
+ })
318
+
319
+ mapCore.offClick({ id: 'station-click' })
320
+
321
+ const point = mapCore.projectToContainerPoint({
322
+ longitude: 104,
323
+ latitude: 35,
324
+ height: 0,
325
+ })
326
+
327
+ const bounds = mapCore.getViewBounds()
328
+ const viewListener = mapCore.onViewChange({
329
+ onStart() {},
330
+ onEnd() {},
331
+ })
332
+ viewListener.data?.off?.()
333
+ ```
334
+
335
+ ### DEM
336
+
337
+ ```js
338
+ mapCore.loadDefaultDEM()
339
+ mapCore.loadDEMById('ellipsoid-flat')
340
+ mapCore.loadDEM({
341
+ id: 'custom-dem',
342
+ sourceType: 'url',
343
+ url: 'https://example.com/terrain',
344
+ options: {},
345
+ })
346
+ ```
347
+
348
+ DEM 当前主要面向 Cesium;Leaflet 调用会返回不支持结果。
349
+
350
+ ## 图层构造器
351
+
352
+ 构造器只返回标准配置对象,通常传给 `config.basemap.list` 或 `mapCore.addLayer()`。
353
+
354
+ ```js
355
+ import {
356
+ createTiandituLayer,
357
+ createGeoserverPyramidLayer,
358
+ createWmsLayer,
359
+ createWmtsLayer,
360
+ } from '@3clear/basegis/layers'
361
+
362
+ const tianditu = createTiandituLayer({
363
+ id: 'tdt-img',
364
+ name: '天地图影像',
365
+ resourceKey: 'imagery',
366
+ category: 'basemap',
367
+ engineSupport: ['cesium', 'leaflet'],
368
+ })
369
+
370
+ const geoserverTile = createGeoserverPyramidLayer({
371
+ id: 'geoserver-tile',
372
+ name: 'GeoServer 瓦片',
373
+ url: 'https://example.com/tiles/{z}/{x}/{y}.png',
374
+ })
375
+
376
+ const wms = createWmsLayer({
377
+ id: 'wms-layer',
378
+ serviceUrl: 'https://example.com/geoserver/wms',
379
+ layers: 'workspace:layer',
380
+ parameters: {
381
+ transparent: true,
382
+ format: 'image/png',
383
+ },
384
+ })
385
+
386
+ const wmts = createWmtsLayer({
387
+ id: 'wmts-layer',
388
+ url: 'https://example.com/wmts?...TILEMATRIX={z}&TILEROW={y}&TILECOL={x}',
389
+ layer: 'workspace:layer',
390
+ tileMatrixSet: 'EPSG:3857',
391
+ })
392
+ ```
393
+
394
+ ## GraphicGroupController
395
+
396
+ 用于一次性加载一组点、线、面、文字、marker。它只调用 `BaseGIS.drawPoint / drawLine / drawPolygon / drawText / addMarker`。
397
+
398
+ ```js
399
+ import { GraphicGroupController } from '@3clear/basegis/methods'
400
+
401
+ const graphics = new GraphicGroupController({ mapCore })
402
+
403
+ graphics.load([
404
+ {
405
+ type: 'point',
406
+ id: 'p1',
407
+ longitude: 104,
408
+ latitude: 35,
409
+ },
410
+ {
411
+ type: 'line',
412
+ id: 'l1',
413
+ positions: [
414
+ [103, 34],
415
+ [105, 36],
416
+ ],
417
+ },
418
+ {
419
+ type: 'text',
420
+ id: 't1',
421
+ longitude: 104,
422
+ latitude: 35,
423
+ text: '站点',
424
+ },
425
+ ])
426
+
427
+ graphics.clear()
428
+ graphics.getState()
429
+ ```
430
+
431
+ ## ImageLayerController
432
+
433
+ 用于加载图片覆盖层或 TIF 图片集合,并提供上一张、下一张、显隐和销毁能力。
434
+
435
+ ```js
436
+ import { ImageLayerController } from '@3clear/basegis/methods'
437
+
438
+ const imageLayer = new ImageLayerController({
439
+ mapCore,
440
+ layerId: 'radar-image',
441
+ opacity: 0.85,
442
+ defaultArea: {
443
+ startLon: 73,
444
+ startLat: 18,
445
+ endLon: 135,
446
+ endLat: 54,
447
+ },
448
+ })
449
+
450
+ await imageLayer.load([
451
+ {
452
+ id: 'radar-001',
453
+ name: '雷达 001',
454
+ imageUrl: '/data/radar/001.png',
455
+ area: {
456
+ startLon: 73,
457
+ startLat: 18,
458
+ endLon: 135,
459
+ endLat: 54,
460
+ },
461
+ opacity: 0.8,
462
+ },
463
+ {
464
+ id: 'tif-001',
465
+ name: 'TIF 001',
466
+ tifUrl: '/data/grid/001.tif',
467
+ colorize: true,
468
+ },
469
+ ])
470
+
471
+ await imageLayer.switchTo('radar-001')
472
+ await imageLayer.next()
473
+ await imageLayer.prev()
474
+ imageLayer.hide()
475
+ imageLayer.show()
476
+ imageLayer.destroy()
477
+ ```
478
+
479
+ 常用字段:
480
+
481
+ - `imageUrl`:普通图片地址。
482
+ - `tifUrl`:GeoTIFF 地址。
483
+ - `area`:图片四至范围,字段为 `startLon / startLat / endLon / endLat`。
484
+ - `opacity`:透明度。
485
+ - `showLabel / showProbe`:显示网格标注或探针。
486
+ - `colorize / colorStops / colors`:色带渲染配置。
487
+
488
+ ## GridLayerController
489
+
490
+ 用于 TIF 网格、灰度图网格、外部数值网格。
491
+
492
+ ```js
493
+ import { GridLayerController } from '@3clear/basegis/methods'
494
+
495
+ const gridLayer = new GridLayerController({
496
+ mapCore,
497
+ layerId: 'temperature-grid',
498
+ showLabel: true,
499
+ showProbe: true,
500
+ decimalPlaces: 1,
501
+ })
502
+
503
+ await gridLayer.loadTif({
504
+ tifUrl: '/data/grid/temp.tif',
505
+ colorize: true,
506
+ })
507
+
508
+ await gridLayer.loadGrayImage({
509
+ imageUrl: '/data/grid/temp-gray.png',
510
+ area: {
511
+ startLon: 73,
512
+ startLat: 18,
513
+ endLon: 135,
514
+ endLat: 54,
515
+ },
516
+ colorize: true,
517
+ })
518
+
519
+ await gridLayer.loadData({
520
+ imageGridData: [
521
+ [12.1, 13.4],
522
+ [14.2, 15.6],
523
+ ],
524
+ area: {
525
+ startLon: 100,
526
+ startLat: 30,
527
+ endLon: 110,
528
+ endLat: 40,
529
+ },
530
+ })
531
+
532
+ gridLayer.update({ opacity: 0.7 })
533
+ gridLayer.hide()
534
+ gridLayer.show()
535
+ gridLayer.destroy()
536
+ ```
537
+
538
+ ## PointLargeLayerController
539
+
540
+ 用于海量点位渲染,支持显隐、高亮、删除单点、批量删除、清空和状态读取。
541
+
542
+ ```js
543
+ import { PointLargeLayerController } from '@3clear/basegis/methods'
544
+
545
+ const largePoints = new PointLargeLayerController({
546
+ mapCore,
547
+ layerId: 'station-large',
548
+ idKey: 'staNum',
549
+ image: '/icons/station.png',
550
+ width: 24,
551
+ height: 24,
552
+ onClick(point) {
553
+ console.log('点击站点', point)
554
+ },
555
+ })
556
+
557
+ await largePoints.load({
558
+ points: [
559
+ { staNum: 'A001', longitude: 104, latitude: 35, name: 'A001' },
560
+ { staNum: 'A002', longitude: 105, latitude: 36, name: 'A002' },
561
+ ],
562
+ })
563
+
564
+ largePoints.setHighlight('A001')
565
+ largePoints.clearHighlight()
566
+ largePoints.removePoint('A002')
567
+ largePoints.removePoints(['A001', 'A003'])
568
+ largePoints.hide()
569
+ largePoints.show()
570
+ largePoints.clear()
571
+ largePoints.destroy()
572
+ largePoints.getState()
573
+ ```
574
+
575
+ 点位字段默认支持:
576
+
577
+ - 经度:`longitude / lon / lng / x`
578
+ - 纬度:`latitude / lat / y`
579
+ - 高度:`height / altitude / z`
580
+ - 唯一值:默认 `id`,可通过 `idKey` 指定
581
+
582
+ ## PointDensityController
583
+
584
+ 用于点位密度抽稀。地图移动、缩放后按屏幕网格重新计算可见点,适合站点、设备、告警等高密度点位。
585
+
586
+ ```js
587
+ import { PointDensityController } from '@3clear/basegis/methods'
588
+
589
+ const density = new PointDensityController({
590
+ mapCore,
591
+ layerId: 'station-density',
592
+ idKey: 'staNum',
593
+ gridSize: 150,
594
+ maxCount: 500,
595
+ priorityKey: 'level',
596
+ image: '/icons/station.png',
597
+ width: 28,
598
+ height: 28,
599
+ onClick(point) {
600
+ console.log(point)
601
+ },
602
+ onStateChange(state) {
603
+ console.log(state.visibleCount, state.hiddenCount)
604
+ },
605
+ })
606
+
607
+ await density.load({
608
+ points: [
609
+ { staNum: 'A001', longitude: 104, latitude: 35, level: 5 },
610
+ { staNum: 'A002', longitude: 104.01, latitude: 35.01, level: 3 },
611
+ ],
612
+ })
613
+
614
+ density.update({ gridSize: 120 })
615
+ density.hide()
616
+ density.show()
617
+ density.toggle()
618
+ density.refreshState()
619
+ density.clear()
620
+ density.destroy()
621
+ ```
622
+
623
+ 常用参数:
624
+
625
+ - `enableThinning`:是否开启抽稀,默认开启。
626
+ - `gridSize / pixelRange`:屏幕网格大小,单位像素。
627
+ - `maxCount`:最多显示点数。
628
+ - `priorityKey / priorityCallback`:同网格内优先显示哪个点。
629
+ - `viewportBuffer`:Cesium 屏幕视野缓冲。
630
+ - `cullByGlobe`:Cesium 3D 下是否剔除地球背面点。
631
+ - `boundsBufferRatio`:Leaflet bounds 扩展比例。
632
+
633
+ ## ContourLayerController
634
+
635
+ 用于等值线图层,支持加载、更新、显隐、清空和销毁。
636
+
637
+ ```js
638
+ import { ContourLayerController } from '@3clear/basegis/methods'
639
+
640
+ const contour = new ContourLayerController({
641
+ mapCore,
642
+ layerId: 'temperature-contour',
643
+ visible: true,
644
+ })
645
+
646
+ await contour.load({
647
+ contours: [
648
+ {
649
+ value: 20,
650
+ lines: [
651
+ [
652
+ [30, 104],
653
+ [31, 105],
654
+ [32, 106],
655
+ ],
656
+ ],
657
+ },
658
+ ],
659
+ style: {
660
+ color: '#ff4d4f',
661
+ width: 2,
662
+ },
663
+ })
664
+
665
+ await contour.update({
666
+ style: {
667
+ color: '#1677ff',
668
+ },
669
+ })
670
+
671
+ contour.hide()
672
+ contour.show()
673
+ contour.clear()
674
+ contour.destroy()
675
+ ```
676
+
677
+ 数据别名支持 `contours / isolines / isoline / lines / data / items`。
678
+
679
+ ## PressureContourLayer
680
+
681
+ 用于等压线和高低压中心标注。`buildPressureRenderData()` 可把 Windy 风格的 `press.json` 数据整理成渲染结构,`createPressureDataResolver()` 提供简单缓存。
682
+
683
+ ```js
684
+ import {
685
+ PressureContourLayer,
686
+ createPressureDataResolver,
687
+ } from '@3clear/basegis/methods'
688
+
689
+ const resolvePressureData = createPressureDataResolver({
690
+ default: pressJson,
691
+ next: pressJson2,
692
+ })
693
+
694
+ const pressureLayer = new PressureContourLayer({
695
+ mapCore,
696
+ layerId: 'pressure',
697
+ color: 'rgba(255,255,255,0.82)',
698
+ width: 1.15,
699
+ smoothFactor: 0.2,
700
+ // Cesium 场景下用于定位容器尺寸,Leaflet 场景也可以传同一个地图容器 id。
701
+ mapContainerId: 'map',
702
+ // 标注层通过该函数读取当前数据,便于地图缩放后重建标签。
703
+ getData: () => resolvePressureData('default'),
704
+ isAnnotationVisible: () => true,
705
+ })
706
+
707
+ pressureLayer.mount()
708
+ await pressureLayer.load(resolvePressureData('default'))
709
+ await pressureLayer.update(resolvePressureData('next'))
710
+ pressureLayer.hide()
711
+ pressureLayer.destroy()
712
+ ```
713
+
714
+ 说明:
715
+
716
+ - 等压线数据会被整理为 `isolines` 和 `centers`。
717
+ - Leaflet 使用 `divIcon` 绘制 H/L 中心和线值标签。
718
+ - Cesium 使用独立标注集合,地图变化时会调度刷新。
719
+
720
+ ## WindFieldMethods
721
+
722
+ 用于叠加 Canvas 风场粒子动画。它通过 `BaseGIS` 获取容器、投影、视野范围和视图变化事件,不直接依赖某个引擎。
723
+
724
+ ```js
725
+ import { WindFieldMethods } from '@3clear/basegis/methods'
726
+
727
+ const wind = new WindFieldMethods({ mapCore })
728
+
729
+ wind.addWindField({
730
+ id: 'wind-main',
731
+ data: windData,
732
+ particleCount: 3000,
733
+ velocityScale: 0.01,
734
+ lineWidth: 1,
735
+ color: 'rgba(80, 180, 255, 0.8)',
736
+ })
737
+
738
+ wind.updateWindField({
739
+ id: 'wind-main',
740
+ particleCount: 5000,
741
+ })
742
+
743
+ wind.stopWindField('wind-main')
744
+ wind.startWindField('wind-main')
745
+ wind.hideWindField('wind-main')
746
+ wind.showWindField('wind-main')
747
+ wind.removeWindField('wind-main')
748
+ wind.clearWindFields()
749
+ wind.getState()
750
+ ```
751
+
752
+ ## 直接使用 BaseGIS 图层方法
753
+
754
+ 控制器适合页面长期维护状态。如果只是一次调用,也可以直接用 `BaseGIS` 方法。
755
+
756
+ ```js
757
+ await mapCore.upsertImageLayer({
758
+ layerId: 'image-layer',
759
+ imageUrl: '/data/image.png',
760
+ area: {
761
+ startLon: 73,
762
+ startLat: 18,
763
+ endLon: 135,
764
+ endLat: 54,
765
+ },
766
+ })
767
+ mapCore.hideImageLayer({ layerId: 'image-layer' })
768
+ mapCore.showImageLayer({ layerId: 'image-layer' })
769
+ mapCore.removeImageLayer({ layerId: 'image-layer' })
770
+
771
+ await mapCore.upsertGridLayer({
772
+ layerId: 'grid-layer',
773
+ tifUrl: '/data/grid.tif',
774
+ })
775
+ mapCore.removeGridLayer({ layerId: 'grid-layer' })
776
+
777
+ await mapCore.upsertPointLargeLayer({
778
+ layerId: 'large-points',
779
+ points: [{ id: '1', longitude: 104, latitude: 35 }],
780
+ })
781
+ mapCore.setPointLargeHighlight({ layerId: 'large-points', pointId: '1' })
782
+ mapCore.clearPointLargeHighlight({ layerId: 'large-points' })
783
+ mapCore.removePointLargePoint({ layerId: 'large-points', pointId: '1' })
784
+ mapCore.clearPointLargeLayer({ layerId: 'large-points' })
785
+ mapCore.removePointLargeLayer({ layerId: 'large-points' })
786
+
787
+ await mapCore.upsertPointDensityLayer({
788
+ layerId: 'density-points',
789
+ points: [{ id: '1', longitude: 104, latitude: 35 }],
790
+ gridSize: 150,
791
+ })
792
+ mapCore.getPointDensityLayerState({ layerId: 'density-points' })
793
+ mapCore.removePointDensityLayer({ layerId: 'density-points' })
794
+
795
+ await mapCore.upsertContourLayer({
796
+ layerId: 'contour',
797
+ contours: [],
798
+ })
799
+ await mapCore.updateContourLayer({ layerId: 'contour', contours: [] })
800
+ mapCore.getContourLayerState({ layerId: 'contour' })
801
+ mapCore.removeContourLayer({ layerId: 'contour' })
802
+ ```
803
+
804
+ ## 在 Vue 中封装 Hook
805
+
806
+ 推荐页面用 hook 管理生命周期,组件只处理 UI。
807
+
808
+ ```js
809
+ import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
810
+ import { BaseGIS } from '@3clear/basegis'
811
+
812
+ export function useBaseGIS(options = {}) {
813
+ const mapCore = shallowRef(null)
814
+
815
+ onMounted(() => {
816
+ mapCore.value = new BaseGIS(options)
817
+ mapCore.value.init()
818
+ })
819
+
820
+ onBeforeUnmount(() => {
821
+ mapCore.value?.destroy()
822
+ mapCore.value = null
823
+ })
824
+
825
+ return {
826
+ mapCore,
827
+ }
828
+ }
829
+ ```
830
+
831
+ 使用:
832
+
833
+ ```vue
834
+ <template>
835
+ <div id="map" class="map"></div>
836
+ </template>
837
+
838
+ <script setup>
839
+ import { useBaseGIS } from './useBaseGIS'
840
+
841
+ const { mapCore } = useBaseGIS({
842
+ engineType: 'cesium',
843
+ containerId: 'map',
844
+ })
845
+ </script>
846
+ ```
847
+
848
+ ## 能力支持说明
849
+
850
+ | 能力 | Cesium | Leaflet |
851
+ | --- | --- | --- |
852
+ | 2D 地图 | 支持 | 支持 |
853
+ | 2.5D / 3D 场景 | 支持 | 不支持 |
854
+ | 放大、缩小、重置 | 支持 | 支持 |
855
+ | 底图切换 | 支持 | 支持 |
856
+ | 点线面文字 | 支持 | 支持 |
857
+ | 图片覆盖层 | 支持 | 支持 |
858
+ | 网格 / TIF | 支持 | 支持 |
859
+ | 海量点 | 支持 | 支持 |
860
+ | 点位密度抽稀 | 支持 | 支持 |
861
+ | 等值线 | 支持 | 支持 |
862
+ | 等压线标注 | 支持 | 支持 |
863
+ | 风场 Canvas 动画 | 支持 | 支持 |
864
+ | DEM 地形 | 支持 | 不支持 |
865
+
866
+ ## 本地开发与打包
867
+
868
+ 在仓库根目录执行:
869
+
870
+ ```bash
871
+ npm run build:basegis
872
+ npm run pack:basegis
873
+ ```
874
+
875
+ `build:basegis` 会生成:
876
+
877
+ ```text
878
+ packages/basegis/dist/basegis.js
879
+ packages/basegis/dist/methods.js
880
+ packages/basegis/dist/layers.js
881
+ packages/basegis/dist/assets.js
882
+ packages/basegis/dist/style.css
883
+ ```
884
+
885
+ `pack:basegis` 会在仓库根目录生成类似文件:
886
+
887
+ ```text
888
+ 3clear-basegis-0.1.0.tgz
889
+ ```
890
+
891
+ 其他项目本地安装:
892
+
893
+ ```bash
894
+ npm install ./3clear-basegis-0.1.0.tgz
895
+ ```
896
+
897
+ ## 发布到 npm
898
+
899
+ 只发布 `packages/basegis`,不要在项目根目录执行 `npm publish`,否则会把整个业务项目上传到 npm。
900
+
901
+ 发布前检查包内容:
902
+
903
+ ```bash
904
+ npm pack --dry-run ./packages/basegis
905
+ ```
906
+
907
+ 发布 scoped 包:
908
+
909
+ ```bash
910
+ npm publish ./packages/basegis --access public
911
+ ```
912
+
913
+ 如果只希望私有发布,保留 `packages/basegis/package.json` 中的:
914
+
915
+ ```json
916
+ {
917
+ "publishConfig": {
918
+ "access": "restricted"
919
+ }
920
+ }
921
+ ```
922
+
923
+ 如果希望公开发布,需要改为:
924
+
925
+ ```json
926
+ {
927
+ "publishConfig": {
928
+ "access": "public"
929
+ }
930
+ }
931
+ ```
932
+
933
+ ## 常见问题
934
+
935
+ ### Cesium 地图不显示
936
+
937
+ 检查宿主项目是否存在:
938
+
939
+ ```text
940
+ public/lib/Cesium/Cesium.js
941
+ public/lib/Cesium/Widgets/widgets.css
942
+ ```
943
+
944
+ 并确认页面初始化前能访问 `window.Cesium`。
945
+
946
+ ### Leaflet 样式异常
947
+
948
+ 确认已引入:
949
+
950
+ ```js
951
+ import '@3clear/basegis/style.css'
952
+ ```
953
+
954
+ ### 调用方法返回 NOT_INITIALIZED
955
+
956
+ 说明地图还没有初始化成功。先执行:
957
+
958
+ ```js
959
+ const result = mapCore.init()
960
+ console.log(result.success, result.message)
961
+ ```
962
+
963
+ ### 切换配置后地图没有变化
964
+
965
+ `setConfig()` 只更新 `BaseGIS` 内部配置,不会自动重建地图。需要销毁后重新初始化:
966
+
967
+ ```js
968
+ mapCore.setConfig(nextConfig)
969
+ mapCore.init()
970
+ ```
971
+
972
+ `init()` 内部会先销毁旧适配器,再创建新实例。