raintee-maputils 1.0.51 → 1.0.53
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/dist/index.js +584 -184
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21429,10 +21429,308 @@ class CustomToggleControl {
|
|
|
21429
21429
|
|
|
21430
21430
|
// RasterLayerController.js
|
|
21431
21431
|
|
|
21432
|
+
// class RasterLayerControl {
|
|
21433
|
+
// constructor() {
|
|
21434
|
+
// this._panel = null;
|
|
21435
|
+
// this._dialog = null;
|
|
21436
|
+
// this._layerListContainer = null;
|
|
21437
|
+
// this._isOpen = false;
|
|
21438
|
+
// this._tempLayers = [];
|
|
21439
|
+
// }
|
|
21440
|
+
|
|
21441
|
+
// onAdd(map) {
|
|
21442
|
+
// this._map = map;
|
|
21443
|
+
|
|
21444
|
+
// this._container = document.createElement('div');
|
|
21445
|
+
// this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapboxgl-ctrl-raster-control';
|
|
21446
|
+
|
|
21447
|
+
// this._button = document.createElement('button');
|
|
21448
|
+
// this._button.type = 'button';
|
|
21449
|
+
// this._button.innerHTML = '底图:初始化中';
|
|
21450
|
+
// this._button.style.cssText = `
|
|
21451
|
+
// width: 100%;
|
|
21452
|
+
// padding: 6px 10px;
|
|
21453
|
+
// margin: 0;
|
|
21454
|
+
// font-size: 14px;
|
|
21455
|
+
// font-family: Arial, sans-serif;
|
|
21456
|
+
// background: #f8f9fa;
|
|
21457
|
+
// color: #333;
|
|
21458
|
+
// border: 1px solid #dee2e6;
|
|
21459
|
+
// border-radius: 4px;
|
|
21460
|
+
// cursor: pointer;
|
|
21461
|
+
// text-align: left;
|
|
21462
|
+
// white-space: nowrap;
|
|
21463
|
+
// box-sizing: border-box;
|
|
21464
|
+
// line-height: 1.4;
|
|
21465
|
+
// `;
|
|
21466
|
+
|
|
21467
|
+
// this._button.addEventListener('mouseenter', () => {
|
|
21468
|
+
// this._button.style.background = '#e9ecef';
|
|
21469
|
+
// });
|
|
21470
|
+
// this._button.addEventListener('mouseleave', () => {
|
|
21471
|
+
// this._button.style.background = '#f8f9fa';
|
|
21472
|
+
// });
|
|
21473
|
+
|
|
21474
|
+
// this._button.addEventListener('click', () => {
|
|
21475
|
+
// this._togglePanel();
|
|
21476
|
+
// });
|
|
21477
|
+
|
|
21478
|
+
// this._container.appendChild(this._button);
|
|
21479
|
+
|
|
21480
|
+
// this._map.on('idle', () => this._updateButtonText());
|
|
21481
|
+
// this._map.on('styledata', () => this._updateButtonText());
|
|
21482
|
+
// this._map.on('sourcedata', () => this._updateButtonText());
|
|
21483
|
+
|
|
21484
|
+
// this._updateButtonText();
|
|
21485
|
+
|
|
21486
|
+
// return this._container;
|
|
21487
|
+
// }
|
|
21488
|
+
|
|
21489
|
+
// onRemove() {
|
|
21490
|
+
// if (this._panel && this._panel.parentNode) {
|
|
21491
|
+
// this._panel.parentNode.removeChild(this._panel);
|
|
21492
|
+
// }
|
|
21493
|
+
// if (this._container && this._container.parentNode) {
|
|
21494
|
+
// this._container.parentNode.removeChild(this._container);
|
|
21495
|
+
// }
|
|
21496
|
+
// this._map = undefined;
|
|
21497
|
+
// }
|
|
21498
|
+
|
|
21499
|
+
// _togglePanel() {
|
|
21500
|
+
// this._isOpen ? this._closePanel() : this._openPanel();
|
|
21501
|
+
// }
|
|
21502
|
+
|
|
21503
|
+
// _openPanel() {
|
|
21504
|
+
// this._createPanelIfNeeded();
|
|
21505
|
+
|
|
21506
|
+
// const layers = this._map.getStyle().layers || [];
|
|
21507
|
+
// const rasterLayers = layers.filter(l => l.type === 'raster');
|
|
21508
|
+
|
|
21509
|
+
// this._tempLayers = rasterLayers.map(l => ({
|
|
21510
|
+
// id: l.id,
|
|
21511
|
+
// visible: this._map.getLayoutProperty(l.id, 'visibility') !== 'none'
|
|
21512
|
+
// }));
|
|
21513
|
+
|
|
21514
|
+
// this._renderLayerList();
|
|
21515
|
+
// this._panel.style.display = 'flex';
|
|
21516
|
+
// this._isOpen = true;
|
|
21517
|
+
// }
|
|
21518
|
+
|
|
21519
|
+
// _closePanel() {
|
|
21520
|
+
// if (this._panel) {
|
|
21521
|
+
// this._panel.style.display = 'none';
|
|
21522
|
+
// }
|
|
21523
|
+
// this._isOpen = false;
|
|
21524
|
+
// }
|
|
21525
|
+
|
|
21526
|
+
// _createPanelIfNeeded() {
|
|
21527
|
+
// if (this._panel) return;
|
|
21528
|
+
|
|
21529
|
+
// // 遮罩
|
|
21530
|
+
// this._panel = document.createElement('div');
|
|
21531
|
+
// this._panel.style.cssText = `
|
|
21532
|
+
// position: fixed;
|
|
21533
|
+
// top: 0; left: 0;
|
|
21534
|
+
// width: 100vw; height: 100vh;
|
|
21535
|
+
// background: rgba(0,0,0,0.45);
|
|
21536
|
+
// display: none;
|
|
21537
|
+
// align-items: center;
|
|
21538
|
+
// justify-content: center;
|
|
21539
|
+
// z-index: 10000;
|
|
21540
|
+
// `;
|
|
21541
|
+
|
|
21542
|
+
|
|
21543
|
+
// // 对话框
|
|
21544
|
+
// this._dialog = document.createElement('div');
|
|
21545
|
+
// this._dialog.style.cssText = `
|
|
21546
|
+
// position: relative;
|
|
21547
|
+
// background: #fff;
|
|
21548
|
+
// padding: 16px;
|
|
21549
|
+
// border-radius: 10px; /* 稍微更柔和 */
|
|
21550
|
+
// min-width: 320px;
|
|
21551
|
+
// max-width: 520px;
|
|
21552
|
+
// max-height: 75vh; /* 🔥 再低一点,留呼吸感 */
|
|
21553
|
+
// overflow: hidden; /* 防止整体溢出 */
|
|
21554
|
+
// display: flex;
|
|
21555
|
+
// flex-direction: column;
|
|
21556
|
+
// box-shadow: 0 6px 18px rgba(0,0,0,.22);
|
|
21557
|
+
// font-family: Arial, sans-serif;
|
|
21558
|
+
// `;
|
|
21559
|
+
|
|
21560
|
+
|
|
21561
|
+
|
|
21562
|
+
|
|
21563
|
+
// // 标题
|
|
21564
|
+
// const title = document.createElement('div');
|
|
21565
|
+
// title.textContent = '🗂 管理栅格底图';
|
|
21566
|
+
// title.style.cssText = `
|
|
21567
|
+
// font-weight: bold;
|
|
21568
|
+
// margin-bottom: 8px;
|
|
21569
|
+
// font-size: 15px;
|
|
21570
|
+
// `;
|
|
21571
|
+
|
|
21572
|
+
// // 列表容器
|
|
21573
|
+
// this._layerListContainer = document.createElement('div');
|
|
21574
|
+
// this._layerListContainer.style.cssText = `
|
|
21575
|
+
// width: 100%;
|
|
21576
|
+
// max-height: 50vh; /* 🔥 核心:最多占屏幕一半高度 */
|
|
21577
|
+
// overflow-y: auto; /* 🔥 超出滚动 */
|
|
21578
|
+
// overflow-x: hidden;
|
|
21579
|
+
// display: flex;
|
|
21580
|
+
// flex-direction: column;
|
|
21581
|
+
// gap: 6px;
|
|
21582
|
+
// padding: 4px 2px;
|
|
21583
|
+
// box-sizing: border-box;
|
|
21584
|
+
// border-top: 1px solid #eee;
|
|
21585
|
+
// border-bottom: 1px solid #eee;
|
|
21586
|
+
// `;
|
|
21587
|
+
|
|
21588
|
+
|
|
21589
|
+
|
|
21590
|
+
// this._dialog.appendChild(title);
|
|
21591
|
+
// this._dialog.appendChild(this._layerListContainer);
|
|
21592
|
+
// this._panel.appendChild(this._dialog);
|
|
21593
|
+
// document.body.appendChild(this._panel);
|
|
21594
|
+
|
|
21595
|
+
// // 点击遮罩关闭
|
|
21596
|
+
// this._panel.addEventListener('click', (e) => {
|
|
21597
|
+
// if (e.target === this._panel) this._closePanel();
|
|
21598
|
+
// });
|
|
21599
|
+
// }
|
|
21600
|
+
|
|
21601
|
+
// _renderLayerList() {
|
|
21602
|
+
// if (!this._layerListContainer) return;
|
|
21603
|
+
// this._layerListContainer.innerHTML = '';
|
|
21604
|
+
|
|
21605
|
+
// if (!this._tempLayers.length) {
|
|
21606
|
+
// this._layerListContainer.textContent = '无可选栅格图层';
|
|
21607
|
+
// return;
|
|
21608
|
+
// }
|
|
21609
|
+
|
|
21610
|
+
// this._tempLayers.forEach((layer, index) => {
|
|
21611
|
+
// const row = document.createElement('div');
|
|
21612
|
+
// row.style.cssText = `
|
|
21613
|
+
// display: flex;
|
|
21614
|
+
// align-items: center;
|
|
21615
|
+
// gap: 6px;
|
|
21616
|
+
// padding: 6px 8px;
|
|
21617
|
+
// background: #f8f9fa;
|
|
21618
|
+
// border-radius: 4px;
|
|
21619
|
+
// `;
|
|
21620
|
+
|
|
21621
|
+
// const checkbox = document.createElement('input');
|
|
21622
|
+
// checkbox.type = 'checkbox';
|
|
21623
|
+
// checkbox.checked = layer.visible;
|
|
21624
|
+
|
|
21625
|
+
// checkbox.onchange = () => {
|
|
21626
|
+
// layer.visible = checkbox.checked;
|
|
21627
|
+
|
|
21628
|
+
// // 🔥 立刻在地图中生效
|
|
21629
|
+
// this._map.setLayoutProperty(
|
|
21630
|
+
// layer.id,
|
|
21631
|
+
// 'visibility',
|
|
21632
|
+
// layer.visible ? 'visible' : 'none'
|
|
21633
|
+
// );
|
|
21634
|
+
|
|
21635
|
+
// // 🔄 同步按钮文字
|
|
21636
|
+
// this._updateButtonText();
|
|
21637
|
+
// };
|
|
21638
|
+
|
|
21639
|
+
|
|
21640
|
+
|
|
21641
|
+
// const label = document.createElement('span');
|
|
21642
|
+
// label.textContent = layer.id;
|
|
21643
|
+
// label.style.cssText = `
|
|
21644
|
+
// flex: 1;
|
|
21645
|
+
// font-size: 13px;
|
|
21646
|
+
// color: #333;
|
|
21647
|
+
// overflow: hidden;
|
|
21648
|
+
// text-overflow: ellipsis;
|
|
21649
|
+
// white-space: nowrap;
|
|
21650
|
+
// `;
|
|
21651
|
+
|
|
21652
|
+
// const upBtn = document.createElement('button');
|
|
21653
|
+
// upBtn.textContent = '↑';
|
|
21654
|
+
// upBtn.disabled = index === 0;
|
|
21655
|
+
// upBtn.onclick = () => this._moveTempLayer(index, index - 1);
|
|
21656
|
+
|
|
21657
|
+
// const downBtn = document.createElement('button');
|
|
21658
|
+
// downBtn.textContent = '↓';
|
|
21659
|
+
// downBtn.disabled = index === this._tempLayers.length - 1;
|
|
21660
|
+
// downBtn.onclick = () => this._moveTempLayer(index, index + 1);
|
|
21661
|
+
|
|
21662
|
+
// [upBtn, downBtn].forEach(btn => {
|
|
21663
|
+
// btn.style.cssText = `
|
|
21664
|
+
// padding: 2px 6px;
|
|
21665
|
+
// border: 1px solid #ccc;
|
|
21666
|
+
// background: #fff;
|
|
21667
|
+
// border-radius: 3px;
|
|
21668
|
+
// cursor: pointer;
|
|
21669
|
+
// `;
|
|
21670
|
+
// });
|
|
21671
|
+
|
|
21672
|
+
// row.appendChild(checkbox);
|
|
21673
|
+
// row.appendChild(label);
|
|
21674
|
+
// row.appendChild(upBtn);
|
|
21675
|
+
// row.appendChild(downBtn);
|
|
21676
|
+
// this._layerListContainer.appendChild(row);
|
|
21677
|
+
// });
|
|
21678
|
+
// }
|
|
21679
|
+
|
|
21680
|
+
// _moveTempLayer(from, to) {
|
|
21681
|
+
// const item = this._tempLayers.splice(from, 1)[0];
|
|
21682
|
+
// this._tempLayers.splice(to, 0, item);
|
|
21683
|
+
|
|
21684
|
+
// // ✅ 按 Mapbox 官方语义:从“最上层”往下重排
|
|
21685
|
+
// for (let i = this._tempLayers.length - 1; i >= 0; i--) {
|
|
21686
|
+
// const layerId = this._tempLayers[i].id;
|
|
21687
|
+
// const beforeId =
|
|
21688
|
+
// i === this._tempLayers.length - 1
|
|
21689
|
+
// ? undefined // 最上面的图层:移到最顶
|
|
21690
|
+
// : this._tempLayers[i + 1].id;
|
|
21691
|
+
|
|
21692
|
+
// // 关键防御:避免 moveLayer 自己到自己前面
|
|
21693
|
+
// if (beforeId && beforeId === layerId) continue;
|
|
21694
|
+
|
|
21695
|
+
// this._map.moveLayer(layerId, beforeId);
|
|
21696
|
+
// }
|
|
21697
|
+
|
|
21698
|
+
// this._renderLayerList();
|
|
21699
|
+
// }
|
|
21700
|
+
|
|
21701
|
+
|
|
21702
|
+
// _applyChanges() {
|
|
21703
|
+
// // 现在不再做任何地图操作,只是关闭面板
|
|
21704
|
+
// this._closePanel();
|
|
21705
|
+
// }
|
|
21706
|
+
|
|
21707
|
+
|
|
21708
|
+
// _updateButtonText() {
|
|
21709
|
+
// if (!this._map) return;
|
|
21710
|
+
|
|
21711
|
+
// const layers = this._map.getStyle().layers || [];
|
|
21712
|
+
// const rasterLayers = layers.filter(l => l.type === 'raster');
|
|
21713
|
+
|
|
21714
|
+
// let firstVisible = null;
|
|
21715
|
+
// for (const layer of rasterLayers) {
|
|
21716
|
+
// if (this._map.getLayoutProperty(layer.id, 'visibility') === 'visible') {
|
|
21717
|
+
// firstVisible = layer.id;
|
|
21718
|
+
// break;
|
|
21719
|
+
// }
|
|
21720
|
+
// }
|
|
21721
|
+
|
|
21722
|
+
// this._button.innerHTML = firstVisible
|
|
21723
|
+
// ? `底图:${firstVisible}`
|
|
21724
|
+
// : rasterLayers.length ? '底图:无' : '底图:无栅格图层';
|
|
21725
|
+
// }
|
|
21726
|
+
// }
|
|
21432
21727
|
class RasterLayerControl {
|
|
21433
21728
|
constructor() {
|
|
21434
21729
|
this._panel = null;
|
|
21730
|
+
this._dialog = null;
|
|
21731
|
+
this._layerListContainer = null;
|
|
21435
21732
|
this._isOpen = false;
|
|
21733
|
+
this._tempLayers = [];
|
|
21436
21734
|
}
|
|
21437
21735
|
|
|
21438
21736
|
onAdd(map) {
|
|
@@ -21445,21 +21743,22 @@ class RasterLayerControl {
|
|
|
21445
21743
|
this._button.type = 'button';
|
|
21446
21744
|
this._button.innerHTML = '底图:初始化中';
|
|
21447
21745
|
this._button.style.cssText = `
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
|
|
21456
|
-
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
|
|
21462
|
-
|
|
21746
|
+
width: 100%;
|
|
21747
|
+
padding: 6px 10px;
|
|
21748
|
+
margin: 0;
|
|
21749
|
+
font-size: 14px;
|
|
21750
|
+
font-family: Arial, sans-serif;
|
|
21751
|
+
background: #f8f9fa;
|
|
21752
|
+
color: #333;
|
|
21753
|
+
border: 1px solid #dee2e6;
|
|
21754
|
+
border-radius: 4px;
|
|
21755
|
+
cursor: pointer;
|
|
21756
|
+
text-align: left;
|
|
21757
|
+
white-space: nowrap;
|
|
21758
|
+
box-sizing: border-box;
|
|
21759
|
+
line-height: 1.4;
|
|
21760
|
+
`;
|
|
21761
|
+
|
|
21463
21762
|
this._button.addEventListener('mouseenter', () => {
|
|
21464
21763
|
this._button.style.background = '#e9ecef';
|
|
21465
21764
|
});
|
|
@@ -21467,26 +21766,17 @@ class RasterLayerControl {
|
|
|
21467
21766
|
this._button.style.background = '#f8f9fa';
|
|
21468
21767
|
});
|
|
21469
21768
|
|
|
21470
|
-
this._container.appendChild(this._button);
|
|
21471
|
-
|
|
21472
21769
|
this._button.addEventListener('click', () => {
|
|
21473
21770
|
this._togglePanel();
|
|
21474
21771
|
});
|
|
21475
21772
|
|
|
21476
|
-
this.
|
|
21477
|
-
this._updateRasterLayers();
|
|
21478
|
-
|
|
21479
|
-
});
|
|
21480
|
-
|
|
21481
|
-
this._map.on('styledata', () => {
|
|
21482
|
-
this._updateRasterLayers();
|
|
21483
|
-
});
|
|
21773
|
+
this._container.appendChild(this._button);
|
|
21484
21774
|
|
|
21485
|
-
this._map.on('
|
|
21486
|
-
|
|
21487
|
-
|
|
21775
|
+
this._map.on('idle', () => this._updateButtonText());
|
|
21776
|
+
this._map.on('styledata', () => this._updateButtonText());
|
|
21777
|
+
this._map.on('sourcedata', () => this._updateButtonText());
|
|
21488
21778
|
|
|
21489
|
-
this.
|
|
21779
|
+
this._updateButtonText();
|
|
21490
21780
|
|
|
21491
21781
|
return this._container;
|
|
21492
21782
|
}
|
|
@@ -21498,20 +21788,26 @@ class RasterLayerControl {
|
|
|
21498
21788
|
if (this._container && this._container.parentNode) {
|
|
21499
21789
|
this._container.parentNode.removeChild(this._container);
|
|
21500
21790
|
}
|
|
21791
|
+
this._map = undefined;
|
|
21501
21792
|
}
|
|
21502
21793
|
|
|
21503
21794
|
_togglePanel() {
|
|
21504
|
-
|
|
21505
|
-
this._closePanel();
|
|
21506
|
-
} else {
|
|
21507
|
-
this._openPanel();
|
|
21508
|
-
}
|
|
21795
|
+
this._isOpen ? this._closePanel() : this._openPanel();
|
|
21509
21796
|
}
|
|
21510
21797
|
|
|
21511
21798
|
_openPanel() {
|
|
21512
21799
|
this._createPanelIfNeeded();
|
|
21513
|
-
|
|
21514
|
-
this.
|
|
21800
|
+
|
|
21801
|
+
const layers = this._map.getStyle().layers || [];
|
|
21802
|
+
const rasterLayers = layers.filter(l => l.type === 'raster');
|
|
21803
|
+
|
|
21804
|
+
this._tempLayers = rasterLayers.map(l => ({
|
|
21805
|
+
id: l.id,
|
|
21806
|
+
visible: this._map.getLayoutProperty(l.id, 'visibility') !== 'none'
|
|
21807
|
+
}));
|
|
21808
|
+
|
|
21809
|
+
this._renderLayerList();
|
|
21810
|
+
this._panel.style.display = 'flex';
|
|
21515
21811
|
this._isOpen = true;
|
|
21516
21812
|
}
|
|
21517
21813
|
|
|
@@ -21523,171 +21819,275 @@ class RasterLayerControl {
|
|
|
21523
21819
|
}
|
|
21524
21820
|
|
|
21525
21821
|
_createPanelIfNeeded() {
|
|
21526
|
-
if (
|
|
21527
|
-
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
21538
|
-
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
|
|
21568
|
-
|
|
21569
|
-
|
|
21570
|
-
this._dialog.appendChild(title);
|
|
21571
|
-
|
|
21572
|
-
// 图层列表容器(原 _layerListContainer)
|
|
21573
|
-
this._layerListContainer = document.createElement('div');
|
|
21574
|
-
this._layerListContainer.style.cssText = `
|
|
21575
|
-
width: 100%;
|
|
21576
|
-
max-height: 60vh;
|
|
21577
|
-
display: flex;
|
|
21578
|
-
flex-direction: column;
|
|
21579
|
-
gap: 4px;
|
|
21580
|
-
overflow-y: auto;
|
|
21581
|
-
overflow-x: hidden;
|
|
21582
|
-
`;
|
|
21583
|
-
this._dialog.appendChild(this._layerListContainer);
|
|
21584
|
-
|
|
21585
|
-
// // 关闭按钮(可选增强)
|
|
21586
|
-
// const closeButton = document.createElement('button');
|
|
21587
|
-
// closeButton.textContent = '✅ 关闭';
|
|
21588
|
-
// closeButton.style.cssText = `
|
|
21589
|
-
// margin-left: auto;
|
|
21590
|
-
// margin-right: auto;
|
|
21591
|
-
// margin-top: 15px;
|
|
21592
|
-
// padding: 6px 12px;
|
|
21593
|
-
// background: #6c757d;
|
|
21594
|
-
// color: white;
|
|
21595
|
-
// border: none;
|
|
21596
|
-
// border-radius: 4px;
|
|
21597
|
-
// cursor: pointer;
|
|
21598
|
-
// `;
|
|
21599
|
-
// closeButton.addEventListener('click', () => {
|
|
21600
|
-
// this._closePanel();
|
|
21601
|
-
// });
|
|
21602
|
-
// this._dialog.appendChild(closeButton);
|
|
21603
|
-
|
|
21604
|
-
this._panel.appendChild(this._dialog);
|
|
21605
|
-
document.body.appendChild(this._panel);
|
|
21606
|
-
|
|
21607
|
-
// 点击背景遮罩关闭弹窗(增强用户体验,可选)
|
|
21608
|
-
this._panel.addEventListener('click', (e) => {
|
|
21609
|
-
// 只有点击背景遮罩部分才关闭,点击对话框内容不关闭
|
|
21610
|
-
if (e.target === this._panel) {
|
|
21611
|
-
this._closePanel();
|
|
21612
|
-
}
|
|
21613
|
-
});
|
|
21614
|
-
}
|
|
21615
|
-
}
|
|
21616
|
-
|
|
21617
|
-
_updateRasterLayers() {
|
|
21822
|
+
if (this._panel) return;
|
|
21823
|
+
|
|
21824
|
+
// 遮罩
|
|
21825
|
+
this._panel = document.createElement('div');
|
|
21826
|
+
this._panel.style.cssText = `
|
|
21827
|
+
position: fixed;
|
|
21828
|
+
top: 0; left: 0;
|
|
21829
|
+
width: 100vw; height: 100vh;
|
|
21830
|
+
background: rgba(0,0,0,0.45);
|
|
21831
|
+
display: none;
|
|
21832
|
+
align-items: center;
|
|
21833
|
+
justify-content: center;
|
|
21834
|
+
z-index: 10000;
|
|
21835
|
+
`;
|
|
21836
|
+
|
|
21837
|
+
|
|
21838
|
+
// 对话框
|
|
21839
|
+
this._dialog = document.createElement('div');
|
|
21840
|
+
this._dialog.style.cssText = `
|
|
21841
|
+
position: relative;
|
|
21842
|
+
background: #fff;
|
|
21843
|
+
padding: 16px;
|
|
21844
|
+
border-radius: 10px; /* 稍微更柔和 */
|
|
21845
|
+
min-width: 320px;
|
|
21846
|
+
max-width: 520px;
|
|
21847
|
+
max-height: 75vh; /* 🔥 再低一点,留呼吸感 */
|
|
21848
|
+
overflow: hidden; /* 防止整体溢出 */
|
|
21849
|
+
display: flex;
|
|
21850
|
+
flex-direction: column;
|
|
21851
|
+
box-shadow: 0 6px 18px rgba(0,0,0,.22);
|
|
21852
|
+
font-family: Arial, sans-serif;
|
|
21853
|
+
`;
|
|
21854
|
+
|
|
21855
|
+
|
|
21856
|
+
|
|
21857
|
+
|
|
21858
|
+
// 标题
|
|
21859
|
+
const title = document.createElement('div');
|
|
21860
|
+
title.textContent = '🗂 管理栅格底图';
|
|
21861
|
+
title.style.cssText = `
|
|
21862
|
+
font-weight: bold;
|
|
21863
|
+
margin-bottom: 8px;
|
|
21864
|
+
font-size: 15px;
|
|
21865
|
+
`;
|
|
21618
21866
|
|
|
21619
|
-
|
|
21620
|
-
|
|
21867
|
+
// 列表容器
|
|
21868
|
+
this._layerListContainer = document.createElement('div');
|
|
21869
|
+
this._layerListContainer.style.cssText = `
|
|
21870
|
+
width: 100%;
|
|
21871
|
+
max-height: 50vh; /* 🔥 核心:最多占屏幕一半高度 */
|
|
21872
|
+
overflow-y: auto; /* 🔥 超出滚动 */
|
|
21873
|
+
overflow-x: hidden;
|
|
21874
|
+
display: flex;
|
|
21875
|
+
flex-direction: column;
|
|
21876
|
+
gap: 6px;
|
|
21877
|
+
padding: 4px 2px;
|
|
21878
|
+
box-sizing: border-box;
|
|
21879
|
+
border-top: 1px solid #eee;
|
|
21880
|
+
border-bottom: 1px solid #eee;
|
|
21881
|
+
`;
|
|
21882
|
+
|
|
21883
|
+
|
|
21884
|
+
|
|
21885
|
+
this._dialog.appendChild(title);
|
|
21886
|
+
this._dialog.appendChild(this._layerListContainer);
|
|
21887
|
+
this._panel.appendChild(this._dialog);
|
|
21888
|
+
document.body.appendChild(this._panel);
|
|
21621
21889
|
|
|
21622
|
-
|
|
21623
|
-
|
|
21890
|
+
// 点击遮罩关闭
|
|
21891
|
+
this._panel.addEventListener('click', (e) => {
|
|
21892
|
+
if (e.target === this._panel) this._closePanel();
|
|
21893
|
+
});
|
|
21894
|
+
}
|
|
21624
21895
|
|
|
21625
|
-
|
|
21626
|
-
|
|
21627
|
-
|
|
21628
|
-
if (visibility === 'visible') {
|
|
21629
|
-
firstVisibleLayerId = layer.id;
|
|
21630
|
-
break;
|
|
21631
|
-
}
|
|
21632
|
-
}
|
|
21896
|
+
_renderLayerList() {
|
|
21897
|
+
if (!this._layerListContainer) return;
|
|
21898
|
+
this._layerListContainer.innerHTML = '';
|
|
21633
21899
|
|
|
21634
|
-
if (
|
|
21635
|
-
|
|
21636
|
-
|
|
21637
|
-
buttonText = '底图:无';
|
|
21900
|
+
if (!this._tempLayers.length) {
|
|
21901
|
+
this._layerListContainer.textContent = '无可选栅格图层';
|
|
21902
|
+
return;
|
|
21638
21903
|
}
|
|
21639
21904
|
|
|
21640
|
-
this.
|
|
21641
|
-
|
|
21642
|
-
// 清空旧的图层列表
|
|
21643
|
-
if (this._layerListContainer) { this._layerListContainer.innerHTML = ''; }
|
|
21644
|
-
rasterLayers.forEach(layer => {
|
|
21645
|
-
const layerId = layer.id;
|
|
21905
|
+
this._tempLayers.forEach((layer, index) => {
|
|
21646
21906
|
const row = document.createElement('div');
|
|
21647
21907
|
row.style.cssText = `
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21908
|
+
display: flex;
|
|
21909
|
+
align-items: center;
|
|
21910
|
+
gap: 6px;
|
|
21911
|
+
padding: 6px 8px;
|
|
21912
|
+
background: #f8f9fa;
|
|
21913
|
+
border-radius: 4px;
|
|
21914
|
+
`;
|
|
21655
21915
|
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21916
|
+
const checkbox = document.createElement('input');
|
|
21917
|
+
checkbox.type = 'checkbox';
|
|
21918
|
+
checkbox.checked = layer.visible;
|
|
21919
|
+
|
|
21920
|
+
checkbox.onchange = () => {
|
|
21921
|
+
layer.visible = checkbox.checked;
|
|
21922
|
+
|
|
21923
|
+
// 🔥 立刻在地图中生效
|
|
21924
|
+
this._map.setLayoutProperty(
|
|
21925
|
+
layer.id,
|
|
21926
|
+
'visibility',
|
|
21927
|
+
layer.visible ? 'visible' : 'none'
|
|
21928
|
+
);
|
|
21929
|
+
|
|
21930
|
+
// 🔄 同步按钮文字
|
|
21931
|
+
this._updateButtonText();
|
|
21932
|
+
};
|
|
21662
21933
|
|
|
21663
|
-
// 更新按钮文字
|
|
21664
|
-
if (this._button) {
|
|
21665
|
-
this._button.innerHTML = `底图:${layerId}`;
|
|
21666
|
-
}
|
|
21667
|
-
this._togglePanel();
|
|
21668
|
-
});
|
|
21669
21934
|
|
|
21670
|
-
// 鼠标 hover 效果
|
|
21671
|
-
row.addEventListener('mouseenter', () => {
|
|
21672
|
-
row.style.background = '#e9ecef';
|
|
21673
|
-
});
|
|
21674
|
-
row.addEventListener('mouseleave', () => {
|
|
21675
|
-
row.style.background = '#f8f9fa';
|
|
21676
|
-
});
|
|
21677
21935
|
|
|
21678
|
-
// 显示图层 ID(可加样式或 icon)
|
|
21679
21936
|
const label = document.createElement('span');
|
|
21680
|
-
label.textContent =
|
|
21681
|
-
label.style.
|
|
21682
|
-
|
|
21683
|
-
|
|
21937
|
+
label.textContent = layer.id;
|
|
21938
|
+
label.style.cssText = `
|
|
21939
|
+
flex: 1;
|
|
21940
|
+
font-size: 13px;
|
|
21941
|
+
color: #333;
|
|
21942
|
+
overflow: hidden;
|
|
21943
|
+
text-overflow: ellipsis;
|
|
21944
|
+
white-space: nowrap;
|
|
21945
|
+
`;
|
|
21946
|
+
|
|
21947
|
+
const upBtn = document.createElement('button');
|
|
21948
|
+
upBtn.textContent = '↑';
|
|
21949
|
+
upBtn.disabled = index === 0;
|
|
21950
|
+
upBtn.onclick = () => this._moveTempLayer(index, index - 1);
|
|
21951
|
+
|
|
21952
|
+
const downBtn = document.createElement('button');
|
|
21953
|
+
downBtn.textContent = '↓';
|
|
21954
|
+
downBtn.disabled = index === this._tempLayers.length - 1;
|
|
21955
|
+
downBtn.onclick = () => this._moveTempLayer(index, index + 1);
|
|
21956
|
+
|
|
21957
|
+
// 定位按钮
|
|
21958
|
+
const locateBtn = document.createElement('button');
|
|
21959
|
+
locateBtn.textContent = '⊕';
|
|
21960
|
+
locateBtn.title = '定位到该图层';
|
|
21961
|
+
let locateInfo = null;
|
|
21962
|
+
try {
|
|
21963
|
+
locateInfo = this._getLayerLocateInfo(layer.id);
|
|
21964
|
+
} catch (e) {
|
|
21965
|
+
console.warn('获取图层定位信息失败:', layer.id, e);
|
|
21966
|
+
}
|
|
21967
|
+
if (!locateInfo) {
|
|
21968
|
+
locateBtn.disabled = true;
|
|
21969
|
+
locateBtn.style.opacity = '0.3';
|
|
21970
|
+
}
|
|
21971
|
+
locateBtn.onclick = () => this._locateToLayer(layer.id, locateBtn);
|
|
21684
21972
|
|
|
21973
|
+
const btnStyle = `padding: 2px 6px; border: 1px solid #ccc; background: #fff; border-radius: 3px; cursor: pointer;`;
|
|
21974
|
+
upBtn.style.cssText = btnStyle;
|
|
21975
|
+
downBtn.style.cssText = btnStyle;
|
|
21976
|
+
locateBtn.style.cssText = locateInfo ? btnStyle : (btnStyle + ' opacity: 0.3; cursor: not-allowed;');
|
|
21977
|
+
|
|
21978
|
+
row.appendChild(checkbox);
|
|
21685
21979
|
row.appendChild(label);
|
|
21686
|
-
|
|
21980
|
+
row.appendChild(locateBtn);
|
|
21981
|
+
row.appendChild(upBtn);
|
|
21982
|
+
row.appendChild(downBtn);
|
|
21983
|
+
this._layerListContainer.appendChild(row);
|
|
21687
21984
|
});
|
|
21688
|
-
|
|
21689
|
-
|
|
21985
|
+
}
|
|
21986
|
+
|
|
21987
|
+
_moveTempLayer(from, to) {
|
|
21988
|
+
const item = this._tempLayers.splice(from, 1)[0];
|
|
21989
|
+
this._tempLayers.splice(to, 0, item);
|
|
21990
|
+
|
|
21991
|
+
// ✅ 按 Mapbox 官方语义:从“最上层”往下重排
|
|
21992
|
+
for (let i = this._tempLayers.length - 1; i >= 0; i--) {
|
|
21993
|
+
const layerId = this._tempLayers[i].id;
|
|
21994
|
+
const beforeId =
|
|
21995
|
+
i === this._tempLayers.length - 1
|
|
21996
|
+
? undefined // 最上面的图层:移到最顶
|
|
21997
|
+
: this._tempLayers[i + 1].id;
|
|
21998
|
+
|
|
21999
|
+
// 关键防御:避免 moveLayer 自己到自己前面
|
|
22000
|
+
if (beforeId && beforeId === layerId) continue;
|
|
22001
|
+
|
|
22002
|
+
this._map.moveLayer(layerId, beforeId);
|
|
22003
|
+
}
|
|
22004
|
+
|
|
22005
|
+
this._renderLayerList();
|
|
22006
|
+
}
|
|
22007
|
+
|
|
22008
|
+
|
|
22009
|
+
_applyChanges() {
|
|
22010
|
+
// 现在不再做任何地图操作,只是关闭面板
|
|
22011
|
+
this._closePanel();
|
|
22012
|
+
}
|
|
22013
|
+
|
|
22014
|
+
|
|
22015
|
+
_updateButtonText() {
|
|
22016
|
+
if (!this._map) return;
|
|
22017
|
+
|
|
22018
|
+
const layers = this._map.getStyle().layers || [];
|
|
22019
|
+
const rasterLayers = layers.filter(l => l.type === 'raster');
|
|
22020
|
+
|
|
22021
|
+
let firstVisible = null;
|
|
22022
|
+
for (const layer of rasterLayers) {
|
|
22023
|
+
if (this._map.getLayoutProperty(layer.id, 'visibility') === 'visible') {
|
|
22024
|
+
firstVisible = layer.id;
|
|
22025
|
+
break;
|
|
22026
|
+
}
|
|
22027
|
+
}
|
|
22028
|
+
|
|
22029
|
+
this._button.innerHTML = firstVisible
|
|
22030
|
+
? `底图:${firstVisible}`
|
|
22031
|
+
: rasterLayers.length ? '底图:无' : '底图:无栅格图层';
|
|
22032
|
+
}
|
|
22033
|
+
|
|
22034
|
+
_getLayerLocateInfo(layerId) {
|
|
22035
|
+
if (!this._map) return null;
|
|
22036
|
+
const style = this._map.getStyle();
|
|
22037
|
+
const layer = style.layers.find(l => l.id === layerId);
|
|
22038
|
+
if (!layer || !layer.source) return null;
|
|
22039
|
+
const source = style.sources[layer.source];
|
|
22040
|
+
if (!source || !source.tiles || !source.tiles.length) return null;
|
|
22041
|
+
const tileUrl = source.tiles[0];
|
|
22042
|
+
// 提取后端基础地址
|
|
22043
|
+
let baseUrl = '';
|
|
22044
|
+
try {
|
|
22045
|
+
const u = new URL(tileUrl, window.location.origin);
|
|
22046
|
+
baseUrl = u.origin;
|
|
22047
|
+
} catch (e) {
|
|
22048
|
+
baseUrl = '';
|
|
22049
|
+
}
|
|
22050
|
+
// 动态栅格
|
|
22051
|
+
const dynMatch = tileUrl.match(/\/raster\/dynamic\/tile\/([^/]+)\//);
|
|
22052
|
+
if (dynMatch) return { type: 'dynamic', name: dynMatch[1], baseUrl };
|
|
22053
|
+
// mbtiles栅格
|
|
22054
|
+
const staticMatch = tileUrl.match(/\/raster\/([^/]+)\//);
|
|
22055
|
+
if (staticMatch && !tileUrl.includes('/dynamic/')) return { type: 'static', name: staticMatch[1], baseUrl };
|
|
22056
|
+
return null;
|
|
22057
|
+
}
|
|
22058
|
+
|
|
22059
|
+
async _locateToLayer(layerId, btn) {
|
|
22060
|
+
const info = this._getLayerLocateInfo(layerId);
|
|
22061
|
+
if (!info) return;
|
|
22062
|
+
const origText = btn.textContent;
|
|
22063
|
+
btn.textContent = '...';
|
|
22064
|
+
btn.disabled = true;
|
|
22065
|
+
try {
|
|
22066
|
+
let url = '';
|
|
22067
|
+
if (info.type === 'static') {
|
|
22068
|
+
url = `${info.baseUrl}/raster/center/${info.name}`;
|
|
22069
|
+
} else {
|
|
22070
|
+
url = `${info.baseUrl}/raster/dynamic/center/${info.name}`;
|
|
22071
|
+
}
|
|
22072
|
+
const resp = await fetch(url);
|
|
22073
|
+
if (!resp.ok) throw new Error('请求失败');
|
|
22074
|
+
const data = await resp.json();
|
|
22075
|
+
if (data.x != null && data.y != null) {
|
|
22076
|
+
this._map.flyTo({
|
|
22077
|
+
center: [data.x, data.y],
|
|
22078
|
+
zoom: data.zoom || 10,
|
|
22079
|
+
duration: 1500
|
|
22080
|
+
});
|
|
22081
|
+
this._closePanel();
|
|
22082
|
+
}
|
|
22083
|
+
} catch (e) {
|
|
22084
|
+
console.error('定位失败:', e);
|
|
22085
|
+
btn.textContent = '✕';
|
|
22086
|
+
setTimeout(() => { btn.textContent = origText; btn.disabled = false; }, 1500);
|
|
22087
|
+
return;
|
|
21690
22088
|
}
|
|
22089
|
+
btn.textContent = origText;
|
|
22090
|
+
btn.disabled = false;
|
|
21691
22091
|
}
|
|
21692
22092
|
}
|
|
21693
22093
|
|