raintee-maputils 1.0.52 → 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 CHANGED
@@ -21429,6 +21429,301 @@ 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;
@@ -21659,18 +21954,30 @@ class RasterLayerControl {
21659
21954
  downBtn.disabled = index === this._tempLayers.length - 1;
21660
21955
  downBtn.onclick = () => this._moveTempLayer(index, index + 1);
21661
21956
 
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
- });
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);
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;');
21671
21977
 
21672
21978
  row.appendChild(checkbox);
21673
21979
  row.appendChild(label);
21980
+ row.appendChild(locateBtn);
21674
21981
  row.appendChild(upBtn);
21675
21982
  row.appendChild(downBtn);
21676
21983
  this._layerListContainer.appendChild(row);
@@ -21723,6 +22030,65 @@ class RasterLayerControl {
21723
22030
  ? `底图:${firstVisible}`
21724
22031
  : rasterLayers.length ? '底图:无' : '底图:无栅格图层';
21725
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;
22088
+ }
22089
+ btn.textContent = origText;
22090
+ btn.disabled = false;
22091
+ }
21726
22092
  }
21727
22093
 
21728
22094
  // terrain-toggle-control.js