@jorgmoritz/gis-manager 0.1.49 → 0.1.51
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.cjs +1501 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +646 -3
- package/dist/index.d.ts +646 -3
- package/dist/index.js +1499 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -13,7 +13,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "@jorgmoritz/gis-manager",
|
|
16
|
-
version: "0.1.
|
|
16
|
+
version: "0.1.51"};
|
|
17
17
|
|
|
18
18
|
// src/utils/version.ts
|
|
19
19
|
var version = package_default.version;
|
|
@@ -1695,6 +1695,765 @@ var CameraManager = class {
|
|
|
1695
1695
|
}
|
|
1696
1696
|
};
|
|
1697
1697
|
|
|
1698
|
+
// src/core/terrain-manager/TerrainManager.ts
|
|
1699
|
+
var TerrainManager = class {
|
|
1700
|
+
constructor(CesiumNS, viewer) {
|
|
1701
|
+
this.CesiumNS = CesiumNS;
|
|
1702
|
+
this.viewer = viewer;
|
|
1703
|
+
/** 已注册的地形源 */
|
|
1704
|
+
__publicField(this, "sources", /* @__PURE__ */ new Map());
|
|
1705
|
+
/** 当前激活的地形源 ID */
|
|
1706
|
+
__publicField(this, "activeSourceId", null);
|
|
1707
|
+
/** 事件发射器 */
|
|
1708
|
+
__publicField(this, "events", new Emitter());
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* 注册地形源
|
|
1712
|
+
* @param config 地形源配置
|
|
1713
|
+
*/
|
|
1714
|
+
register(config) {
|
|
1715
|
+
if (this.sources.has(config.id)) {
|
|
1716
|
+
console.warn(`[TerrainManager] \u5730\u5F62\u6E90 "${config.id}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u88AB\u8986\u76D6`);
|
|
1717
|
+
}
|
|
1718
|
+
const normalizedConfig = {
|
|
1719
|
+
requestVertexNormals: true,
|
|
1720
|
+
requestWaterMask: true,
|
|
1721
|
+
priority: 0,
|
|
1722
|
+
...config
|
|
1723
|
+
};
|
|
1724
|
+
this.sources.set(config.id, normalizedConfig);
|
|
1725
|
+
this.events.emit({ registered: { config: normalizedConfig } });
|
|
1726
|
+
console.log(`[TerrainManager] \u5DF2\u6CE8\u518C\u5730\u5F62\u6E90: ${config.id}`);
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* 批量注册地形源
|
|
1730
|
+
* @param configs 地形源配置数组
|
|
1731
|
+
*/
|
|
1732
|
+
registerBatch(configs) {
|
|
1733
|
+
configs.forEach((config) => this.register(config));
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* 切换到指定地形源
|
|
1737
|
+
* @param sourceId 地形源 ID
|
|
1738
|
+
* @param options 切换选项
|
|
1739
|
+
*/
|
|
1740
|
+
async switchTo(sourceId, options) {
|
|
1741
|
+
const config = this.sources.get(sourceId);
|
|
1742
|
+
if (!config) {
|
|
1743
|
+
const error = `\u5730\u5F62\u6E90 "${sourceId}" \u672A\u6CE8\u518C`;
|
|
1744
|
+
console.error(`[TerrainManager] ${error}`);
|
|
1745
|
+
return { success: false, activeId: this.activeSourceId, error };
|
|
1746
|
+
}
|
|
1747
|
+
try {
|
|
1748
|
+
const provider = await this.createTerrainProvider(config);
|
|
1749
|
+
if (!provider) {
|
|
1750
|
+
throw new Error("\u65E0\u6CD5\u521B\u5EFA\u5730\u5F62\u63D0\u4F9B\u8005");
|
|
1751
|
+
}
|
|
1752
|
+
const previousId = this.activeSourceId;
|
|
1753
|
+
this.viewer.terrainProvider = provider;
|
|
1754
|
+
this.activeSourceId = sourceId;
|
|
1755
|
+
this.events.emit({ switched: { fromId: previousId, toId: sourceId } });
|
|
1756
|
+
console.log(`[TerrainManager] \u5DF2\u5207\u6362\u5730\u5F62: ${previousId} -> ${sourceId}`);
|
|
1757
|
+
if (options?.flyTo && config.bounds) {
|
|
1758
|
+
await this.flyToBounds(config.bounds, options.flyDuration);
|
|
1759
|
+
}
|
|
1760
|
+
return { success: true, activeId: sourceId };
|
|
1761
|
+
} catch (error) {
|
|
1762
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1763
|
+
console.error(`[TerrainManager] \u5207\u6362\u5730\u5F62\u5931\u8D25:`, error);
|
|
1764
|
+
this.events.emit({ error: { id: sourceId, error } });
|
|
1765
|
+
return { success: false, activeId: this.activeSourceId, error: errMsg };
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* 获取当前激活的地形源配置
|
|
1770
|
+
*/
|
|
1771
|
+
getActive() {
|
|
1772
|
+
if (!this.activeSourceId) return null;
|
|
1773
|
+
return this.sources.get(this.activeSourceId) ?? null;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* 获取当前激活的地形源 ID
|
|
1777
|
+
*/
|
|
1778
|
+
getActiveId() {
|
|
1779
|
+
return this.activeSourceId;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* 获取指定地形源配置
|
|
1783
|
+
* @param sourceId 地形源 ID
|
|
1784
|
+
*/
|
|
1785
|
+
get(sourceId) {
|
|
1786
|
+
return this.sources.get(sourceId);
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* 获取所有已注册的地形源
|
|
1790
|
+
*/
|
|
1791
|
+
list() {
|
|
1792
|
+
return Array.from(this.sources.values());
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* 检查地形源是否已注册
|
|
1796
|
+
* @param sourceId 地形源 ID
|
|
1797
|
+
*/
|
|
1798
|
+
has(sourceId) {
|
|
1799
|
+
return this.sources.has(sourceId);
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* 移除地形源
|
|
1803
|
+
* @param sourceId 地形源 ID
|
|
1804
|
+
*/
|
|
1805
|
+
remove(sourceId) {
|
|
1806
|
+
if (!this.sources.has(sourceId)) {
|
|
1807
|
+
return false;
|
|
1808
|
+
}
|
|
1809
|
+
if (this.activeSourceId === sourceId) {
|
|
1810
|
+
this.reset();
|
|
1811
|
+
}
|
|
1812
|
+
this.sources.delete(sourceId);
|
|
1813
|
+
this.events.emit({ removed: { id: sourceId } });
|
|
1814
|
+
console.log(`[TerrainManager] \u5DF2\u79FB\u9664\u5730\u5F62\u6E90: ${sourceId}`);
|
|
1815
|
+
return true;
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* 重置为椭球体(无地形)
|
|
1819
|
+
*/
|
|
1820
|
+
reset() {
|
|
1821
|
+
const C = this.CesiumNS;
|
|
1822
|
+
const previousId = this.activeSourceId;
|
|
1823
|
+
this.viewer.terrainProvider = new C.EllipsoidTerrainProvider();
|
|
1824
|
+
this.activeSourceId = null;
|
|
1825
|
+
this.events.emit({ switched: { fromId: previousId, toId: null } });
|
|
1826
|
+
console.log(`[TerrainManager] \u5DF2\u91CD\u7F6E\u5730\u5F62\u4E3A\u692D\u7403\u4F53`);
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* 清除所有已注册的地形源
|
|
1830
|
+
*/
|
|
1831
|
+
clear() {
|
|
1832
|
+
this.reset();
|
|
1833
|
+
this.sources.clear();
|
|
1834
|
+
console.log(`[TerrainManager] \u5DF2\u6E05\u9664\u6240\u6709\u5730\u5F62\u6E90`);
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* 销毁管理器
|
|
1838
|
+
*/
|
|
1839
|
+
destroy() {
|
|
1840
|
+
this.clear();
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* 创建地形提供者
|
|
1844
|
+
*/
|
|
1845
|
+
async createTerrainProvider(config) {
|
|
1846
|
+
const C = this.CesiumNS;
|
|
1847
|
+
switch (config.providerType) {
|
|
1848
|
+
case "ellipsoid":
|
|
1849
|
+
return new C.EllipsoidTerrainProvider();
|
|
1850
|
+
case "cesium-ion": {
|
|
1851
|
+
const assetId = config.ionAssetId ?? 1;
|
|
1852
|
+
const IonResource = C.IonResource ?? C.Resource;
|
|
1853
|
+
if (IonResource?.fromAssetId) {
|
|
1854
|
+
const resource = await IonResource.fromAssetId(assetId);
|
|
1855
|
+
return await C.CesiumTerrainProvider.fromUrl(resource, {
|
|
1856
|
+
requestVertexNormals: config.requestVertexNormals,
|
|
1857
|
+
requestWaterMask: config.requestWaterMask
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
if (C.createWorldTerrain) {
|
|
1861
|
+
return C.createWorldTerrain({
|
|
1862
|
+
requestVertexNormals: config.requestVertexNormals,
|
|
1863
|
+
requestWaterMask: config.requestWaterMask
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
return void 0;
|
|
1867
|
+
}
|
|
1868
|
+
case "url": {
|
|
1869
|
+
if (!config.url) {
|
|
1870
|
+
throw new Error("URL \u7C7B\u578B\u5730\u5F62\u6E90\u5FC5\u987B\u63D0\u4F9B url");
|
|
1871
|
+
}
|
|
1872
|
+
return await C.CesiumTerrainProvider.fromUrl(config.url, {
|
|
1873
|
+
requestVertexNormals: config.requestVertexNormals,
|
|
1874
|
+
requestWaterMask: config.requestWaterMask
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
default:
|
|
1878
|
+
return void 0;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
/**
|
|
1882
|
+
* 飞行到指定范围
|
|
1883
|
+
*/
|
|
1884
|
+
async flyToBounds(bounds, duration = 2) {
|
|
1885
|
+
const C = this.CesiumNS;
|
|
1886
|
+
const [west, south, east, north] = bounds;
|
|
1887
|
+
const centerLon = (west + east) / 2;
|
|
1888
|
+
const centerLat = (south + north) / 2;
|
|
1889
|
+
const lonSpan = Math.abs(east - west);
|
|
1890
|
+
const latSpan = Math.abs(north - south);
|
|
1891
|
+
const maxSpan = Math.max(lonSpan, latSpan);
|
|
1892
|
+
const cameraHeight = Math.max(maxSpan * 111e3 * 1.5, 1e3);
|
|
1893
|
+
let terrainHeight = 0;
|
|
1894
|
+
try {
|
|
1895
|
+
const terrainProvider = this.viewer.terrainProvider;
|
|
1896
|
+
if (terrainProvider && !(terrainProvider instanceof C.EllipsoidTerrainProvider)) {
|
|
1897
|
+
const positions = [C.Cartographic.fromDegrees(centerLon, centerLat)];
|
|
1898
|
+
const sampledPositions = await C.sampleTerrainMostDetailed(terrainProvider, positions);
|
|
1899
|
+
terrainHeight = sampledPositions[0]?.height || 0;
|
|
1900
|
+
}
|
|
1901
|
+
} catch {
|
|
1902
|
+
}
|
|
1903
|
+
this.viewer.camera.flyTo({
|
|
1904
|
+
destination: C.Cartesian3.fromDegrees(centerLon, centerLat, terrainHeight + cameraHeight),
|
|
1905
|
+
orientation: {
|
|
1906
|
+
heading: C.Math.toRadians(0),
|
|
1907
|
+
pitch: C.Math.toRadians(-45),
|
|
1908
|
+
roll: 0
|
|
1909
|
+
},
|
|
1910
|
+
duration
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
// src/core/imagery-manager/ImageryManager.ts
|
|
1916
|
+
var ImageryManager = class {
|
|
1917
|
+
constructor(CesiumNS, viewer) {
|
|
1918
|
+
this.CesiumNS = CesiumNS;
|
|
1919
|
+
this.viewer = viewer;
|
|
1920
|
+
/** 已加载的图层 */
|
|
1921
|
+
__publicField(this, "layers", /* @__PURE__ */ new Map());
|
|
1922
|
+
/** 事件发射器 */
|
|
1923
|
+
__publicField(this, "events", new Emitter());
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* 添加影像图层
|
|
1927
|
+
* @param config 图层配置
|
|
1928
|
+
* @param options 加载选项
|
|
1929
|
+
*/
|
|
1930
|
+
async add(config, options) {
|
|
1931
|
+
if (this.layers.has(config.id)) {
|
|
1932
|
+
console.warn(`[ImageryManager] \u56FE\u5C42 "${config.id}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u88AB\u66FF\u6362`);
|
|
1933
|
+
this.remove(config.id);
|
|
1934
|
+
}
|
|
1935
|
+
try {
|
|
1936
|
+
const provider = this.createImageryProvider(config);
|
|
1937
|
+
if (!provider) {
|
|
1938
|
+
throw new Error("\u65E0\u6CD5\u521B\u5EFA\u5F71\u50CF\u63D0\u4F9B\u8005");
|
|
1939
|
+
}
|
|
1940
|
+
const layer = this.viewer.imageryLayers.addImageryProvider(provider);
|
|
1941
|
+
layer.alpha = config.opacity ?? 1;
|
|
1942
|
+
layer.show = config.show ?? true;
|
|
1943
|
+
layer._customId = config.id;
|
|
1944
|
+
this.layers.set(config.id, { config, layer });
|
|
1945
|
+
this.applyZIndex(config.id, config.zIndex ?? 0);
|
|
1946
|
+
this.events.emit({ added: { config } });
|
|
1947
|
+
console.log(`[ImageryManager] \u5DF2\u6DFB\u52A0\u56FE\u5C42: ${config.id}`);
|
|
1948
|
+
if (options?.flyTo && config.bounds) {
|
|
1949
|
+
await this.flyToBounds(config.bounds, options.flyDuration);
|
|
1950
|
+
}
|
|
1951
|
+
return { id: config.id, success: true };
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1954
|
+
console.error(`[ImageryManager] \u6DFB\u52A0\u56FE\u5C42\u5931\u8D25:`, error);
|
|
1955
|
+
this.events.emit({ error: { id: config.id, error } });
|
|
1956
|
+
return { id: config.id, success: false, error: errMsg };
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* 批量添加影像图层
|
|
1961
|
+
* @param configs 图层配置数组
|
|
1962
|
+
* @param options 加载选项
|
|
1963
|
+
*/
|
|
1964
|
+
async addBatch(configs, options) {
|
|
1965
|
+
const sorted = [...configs].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
1966
|
+
const results = [];
|
|
1967
|
+
for (const config of sorted) {
|
|
1968
|
+
const result = await this.add(config, options);
|
|
1969
|
+
results.push(result);
|
|
1970
|
+
}
|
|
1971
|
+
return results;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* 移除影像图层
|
|
1975
|
+
* @param layerId 图层 ID
|
|
1976
|
+
*/
|
|
1977
|
+
remove(layerId) {
|
|
1978
|
+
const entry = this.layers.get(layerId);
|
|
1979
|
+
if (!entry) {
|
|
1980
|
+
return false;
|
|
1981
|
+
}
|
|
1982
|
+
this.viewer.imageryLayers.remove(entry.layer, true);
|
|
1983
|
+
this.layers.delete(layerId);
|
|
1984
|
+
this.events.emit({ removed: { id: layerId } });
|
|
1985
|
+
console.log(`[ImageryManager] \u5DF2\u79FB\u9664\u56FE\u5C42: ${layerId}`);
|
|
1986
|
+
return true;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* 设置图层可见性
|
|
1990
|
+
* @param layerId 图层 ID
|
|
1991
|
+
* @param visible 是否可见
|
|
1992
|
+
*/
|
|
1993
|
+
setVisible(layerId, visible) {
|
|
1994
|
+
const entry = this.layers.get(layerId);
|
|
1995
|
+
if (!entry) {
|
|
1996
|
+
return false;
|
|
1997
|
+
}
|
|
1998
|
+
entry.layer.show = visible;
|
|
1999
|
+
entry.config.show = visible;
|
|
2000
|
+
this.events.emit({ visibilityChanged: { id: layerId, visible } });
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* 设置图层透明度
|
|
2005
|
+
* @param layerId 图层 ID
|
|
2006
|
+
* @param opacity 透明度 0-1
|
|
2007
|
+
*/
|
|
2008
|
+
setOpacity(layerId, opacity) {
|
|
2009
|
+
const entry = this.layers.get(layerId);
|
|
2010
|
+
if (!entry) {
|
|
2011
|
+
return false;
|
|
2012
|
+
}
|
|
2013
|
+
const clampedOpacity = Math.max(0, Math.min(1, opacity));
|
|
2014
|
+
entry.layer.alpha = clampedOpacity;
|
|
2015
|
+
entry.config.opacity = clampedOpacity;
|
|
2016
|
+
this.events.emit({ opacityChanged: { id: layerId, opacity: clampedOpacity } });
|
|
2017
|
+
return true;
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* 调整图层顺序
|
|
2021
|
+
* @param layerId 图层 ID
|
|
2022
|
+
* @param zIndex 新的层级
|
|
2023
|
+
*/
|
|
2024
|
+
reorder(layerId, zIndex) {
|
|
2025
|
+
const entry = this.layers.get(layerId);
|
|
2026
|
+
if (!entry) {
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
entry.config.zIndex = zIndex;
|
|
2030
|
+
this.applyZIndex(layerId, zIndex);
|
|
2031
|
+
this.events.emit({ orderChanged: { id: layerId, zIndex } });
|
|
2032
|
+
return true;
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* 将图层移到最上层
|
|
2036
|
+
* @param layerId 图层 ID
|
|
2037
|
+
*/
|
|
2038
|
+
bringToTop(layerId) {
|
|
2039
|
+
const entry = this.layers.get(layerId);
|
|
2040
|
+
if (!entry) {
|
|
2041
|
+
return false;
|
|
2042
|
+
}
|
|
2043
|
+
const imageryLayers = this.viewer.imageryLayers;
|
|
2044
|
+
const currentIndex = imageryLayers.indexOf(entry.layer);
|
|
2045
|
+
if (currentIndex >= 0 && currentIndex < imageryLayers.length - 1) {
|
|
2046
|
+
for (let i = currentIndex; i < imageryLayers.length - 1; i++) {
|
|
2047
|
+
imageryLayers.raise(entry.layer);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
const maxZIndex = Math.max(...Array.from(this.layers.values()).map((e) => e.config.zIndex ?? 0));
|
|
2051
|
+
entry.config.zIndex = maxZIndex + 1;
|
|
2052
|
+
this.events.emit({ orderChanged: { id: layerId, zIndex: entry.config.zIndex } });
|
|
2053
|
+
return true;
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* 将图层移到最下层
|
|
2057
|
+
* @param layerId 图层 ID
|
|
2058
|
+
*/
|
|
2059
|
+
sendToBottom(layerId) {
|
|
2060
|
+
const entry = this.layers.get(layerId);
|
|
2061
|
+
if (!entry) {
|
|
2062
|
+
return false;
|
|
2063
|
+
}
|
|
2064
|
+
const imageryLayers = this.viewer.imageryLayers;
|
|
2065
|
+
const currentIndex = imageryLayers.indexOf(entry.layer);
|
|
2066
|
+
if (currentIndex > 0) {
|
|
2067
|
+
for (let i = currentIndex; i > 0; i--) {
|
|
2068
|
+
imageryLayers.lower(entry.layer);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
const minZIndex = Math.min(...Array.from(this.layers.values()).map((e) => e.config.zIndex ?? 0));
|
|
2072
|
+
entry.config.zIndex = minZIndex - 1;
|
|
2073
|
+
this.events.emit({ orderChanged: { id: layerId, zIndex: entry.config.zIndex } });
|
|
2074
|
+
return true;
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* 获取图层配置
|
|
2078
|
+
* @param layerId 图层 ID
|
|
2079
|
+
*/
|
|
2080
|
+
get(layerId) {
|
|
2081
|
+
return this.layers.get(layerId)?.config;
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* 获取底层 Cesium ImageryLayer
|
|
2085
|
+
* @param layerId 图层 ID
|
|
2086
|
+
*/
|
|
2087
|
+
getLayer(layerId) {
|
|
2088
|
+
return this.layers.get(layerId)?.layer;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* 获取所有图层配置
|
|
2092
|
+
*/
|
|
2093
|
+
list() {
|
|
2094
|
+
return Array.from(this.layers.values()).map((e) => e.config);
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* 获取所有底图图层
|
|
2098
|
+
*/
|
|
2099
|
+
listBaseLayers() {
|
|
2100
|
+
return this.list().filter((c) => c.layerType === "base");
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* 获取所有叠加图层
|
|
2104
|
+
*/
|
|
2105
|
+
listOverlays() {
|
|
2106
|
+
return this.list().filter((c) => c.layerType === "overlay");
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* 检查图层是否存在
|
|
2110
|
+
* @param layerId 图层 ID
|
|
2111
|
+
*/
|
|
2112
|
+
has(layerId) {
|
|
2113
|
+
return this.layers.has(layerId);
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
2116
|
+
* 清除所有叠加层(保留底图)
|
|
2117
|
+
*/
|
|
2118
|
+
clearOverlays() {
|
|
2119
|
+
const overlays = this.listOverlays();
|
|
2120
|
+
overlays.forEach((config) => this.remove(config.id));
|
|
2121
|
+
console.log(`[ImageryManager] \u5DF2\u6E05\u9664 ${overlays.length} \u4E2A\u53E0\u52A0\u5C42`);
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* 清除所有图层
|
|
2125
|
+
*/
|
|
2126
|
+
clear() {
|
|
2127
|
+
const ids = Array.from(this.layers.keys());
|
|
2128
|
+
ids.forEach((id) => this.remove(id));
|
|
2129
|
+
console.log(`[ImageryManager] \u5DF2\u6E05\u9664\u6240\u6709\u56FE\u5C42`);
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* 销毁管理器
|
|
2133
|
+
*/
|
|
2134
|
+
destroy() {
|
|
2135
|
+
this.clear();
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* 创建影像提供者
|
|
2139
|
+
*/
|
|
2140
|
+
createImageryProvider(config) {
|
|
2141
|
+
const C = this.CesiumNS;
|
|
2142
|
+
const autoDetectedConfig = this.autoDetectTiandituWmts(config);
|
|
2143
|
+
let rectangle;
|
|
2144
|
+
if (autoDetectedConfig.bounds) {
|
|
2145
|
+
let bounds = autoDetectedConfig.bounds;
|
|
2146
|
+
if (Math.abs(bounds[1]) > 90 && Math.abs(bounds[2]) <= 90) {
|
|
2147
|
+
bounds = [bounds[0], bounds[2], bounds[1], bounds[3]];
|
|
2148
|
+
}
|
|
2149
|
+
rectangle = C.Rectangle.fromDegrees(bounds[0], bounds[1], bounds[2], bounds[3]);
|
|
2150
|
+
}
|
|
2151
|
+
const minLevel = autoDetectedConfig.zoomRange?.min;
|
|
2152
|
+
const maxLevel = autoDetectedConfig.zoomRange?.max;
|
|
2153
|
+
switch (autoDetectedConfig.provider) {
|
|
2154
|
+
case "urlTemplate":
|
|
2155
|
+
case "tms":
|
|
2156
|
+
return new C.UrlTemplateImageryProvider({
|
|
2157
|
+
url: autoDetectedConfig.url,
|
|
2158
|
+
minimumLevel: minLevel,
|
|
2159
|
+
maximumLevel: maxLevel,
|
|
2160
|
+
rectangle,
|
|
2161
|
+
subdomains: autoDetectedConfig.subdomains
|
|
2162
|
+
});
|
|
2163
|
+
case "wmts":
|
|
2164
|
+
if (!autoDetectedConfig.wmts) {
|
|
2165
|
+
throw new Error("WMTS \u63D0\u4F9B\u8005\u9700\u8981 wmts \u914D\u7F6E");
|
|
2166
|
+
}
|
|
2167
|
+
return new C.WebMapTileServiceImageryProvider({
|
|
2168
|
+
url: autoDetectedConfig.url,
|
|
2169
|
+
layer: autoDetectedConfig.wmts.layer,
|
|
2170
|
+
style: autoDetectedConfig.wmts.style ?? "default",
|
|
2171
|
+
tileMatrixSetID: autoDetectedConfig.wmts.tileMatrixSetID,
|
|
2172
|
+
format: autoDetectedConfig.wmts.format ?? "image/png",
|
|
2173
|
+
subdomains: autoDetectedConfig.subdomains,
|
|
2174
|
+
tilingScheme: new C.WebMercatorTilingScheme(),
|
|
2175
|
+
maximumLevel: maxLevel
|
|
2176
|
+
});
|
|
2177
|
+
case "wms":
|
|
2178
|
+
if (!autoDetectedConfig.wms) {
|
|
2179
|
+
throw new Error("WMS \u63D0\u4F9B\u8005\u9700\u8981 wms \u914D\u7F6E");
|
|
2180
|
+
}
|
|
2181
|
+
return new C.WebMapServiceImageryProvider({
|
|
2182
|
+
url: autoDetectedConfig.url,
|
|
2183
|
+
layers: autoDetectedConfig.wms.layers,
|
|
2184
|
+
parameters: autoDetectedConfig.wms.parameters,
|
|
2185
|
+
rectangle
|
|
2186
|
+
});
|
|
2187
|
+
case "arcgis":
|
|
2188
|
+
return new C.ArcGisMapServerImageryProvider({
|
|
2189
|
+
url: autoDetectedConfig.url
|
|
2190
|
+
});
|
|
2191
|
+
default:
|
|
2192
|
+
return void 0;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* 自动检测天地图 WMTS 服务并补充配置
|
|
2197
|
+
*/
|
|
2198
|
+
autoDetectTiandituWmts(config) {
|
|
2199
|
+
const url = config.url;
|
|
2200
|
+
const isTiandituWmts = url.includes("tianditu.gov.cn") && url.includes("wmts");
|
|
2201
|
+
if (!isTiandituWmts) {
|
|
2202
|
+
return config;
|
|
2203
|
+
}
|
|
2204
|
+
if (config.provider === "wmts" && config.wmts) {
|
|
2205
|
+
return config;
|
|
2206
|
+
}
|
|
2207
|
+
const tk = url.match(/tk=([^&]+)/)?.[1] || "f44fcec420c3eb1c56656e9a22dabb17";
|
|
2208
|
+
let layer = "img";
|
|
2209
|
+
let baseUrl = url;
|
|
2210
|
+
if (url.includes("/img_w/")) {
|
|
2211
|
+
layer = "img";
|
|
2212
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/img_w/wmts?tk=${tk}`;
|
|
2213
|
+
} else if (url.includes("/cia_w/")) {
|
|
2214
|
+
layer = "cia";
|
|
2215
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/cia_w/wmts?tk=${tk}`;
|
|
2216
|
+
} else if (url.includes("/vec_w/")) {
|
|
2217
|
+
layer = "vec";
|
|
2218
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/vec_w/wmts?tk=${tk}`;
|
|
2219
|
+
} else if (url.includes("/cva_w/")) {
|
|
2220
|
+
layer = "cva";
|
|
2221
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/cva_w/wmts?tk=${tk}`;
|
|
2222
|
+
} else if (url.includes("/ter_w/")) {
|
|
2223
|
+
layer = "ter";
|
|
2224
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/ter_w/wmts?tk=${tk}`;
|
|
2225
|
+
} else if (url.includes("/cta_w/")) {
|
|
2226
|
+
layer = "cta";
|
|
2227
|
+
baseUrl = `https://t{s}.tianditu.gov.cn/cta_w/wmts?tk=${tk}`;
|
|
2228
|
+
}
|
|
2229
|
+
console.log(`[ImageryManager] \u81EA\u52A8\u68C0\u6D4B\u5230\u5929\u5730\u56FE WMTS \u670D\u52A1: layer=${layer}`);
|
|
2230
|
+
return {
|
|
2231
|
+
...config,
|
|
2232
|
+
url: baseUrl,
|
|
2233
|
+
provider: "wmts",
|
|
2234
|
+
subdomains: config.subdomains || ["0", "1", "2", "3", "4", "5", "6", "7"],
|
|
2235
|
+
wmts: {
|
|
2236
|
+
layer,
|
|
2237
|
+
style: "default",
|
|
2238
|
+
tileMatrixSetID: "w",
|
|
2239
|
+
format: "tiles"
|
|
2240
|
+
},
|
|
2241
|
+
zoomRange: config.zoomRange || { max: 18 }
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
/**
|
|
2245
|
+
* 应用图层顺序
|
|
2246
|
+
*/
|
|
2247
|
+
applyZIndex(layerId, zIndex) {
|
|
2248
|
+
const entry = this.layers.get(layerId);
|
|
2249
|
+
if (!entry) return;
|
|
2250
|
+
const imageryLayers = this.viewer.imageryLayers;
|
|
2251
|
+
const currentIndex = imageryLayers.indexOf(entry.layer);
|
|
2252
|
+
if (currentIndex < 0) return;
|
|
2253
|
+
const sortedEntries = Array.from(this.layers.entries()).map(([id, e]) => ({ id, zIndex: e.config.zIndex ?? 0, layer: e.layer })).sort((a, b) => a.zIndex - b.zIndex);
|
|
2254
|
+
const targetIndex = sortedEntries.findIndex((e) => e.id === layerId);
|
|
2255
|
+
if (targetIndex < 0) return;
|
|
2256
|
+
const diff = targetIndex - currentIndex;
|
|
2257
|
+
if (diff > 0) {
|
|
2258
|
+
for (let i = 0; i < diff; i++) {
|
|
2259
|
+
imageryLayers.raise(entry.layer);
|
|
2260
|
+
}
|
|
2261
|
+
} else if (diff < 0) {
|
|
2262
|
+
for (let i = 0; i < -diff; i++) {
|
|
2263
|
+
imageryLayers.lower(entry.layer);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
/**
|
|
2268
|
+
* 飞行到指定范围
|
|
2269
|
+
*/
|
|
2270
|
+
async flyToBounds(bounds, duration = 2) {
|
|
2271
|
+
const C = this.CesiumNS;
|
|
2272
|
+
const [west, south, east, north] = bounds;
|
|
2273
|
+
const centerLon = (west + east) / 2;
|
|
2274
|
+
const centerLat = (south + north) / 2;
|
|
2275
|
+
const lonSpan = Math.abs(east - west);
|
|
2276
|
+
const latSpan = Math.abs(north - south);
|
|
2277
|
+
const maxSpan = Math.max(lonSpan, latSpan);
|
|
2278
|
+
const cameraHeight = Math.max(maxSpan * 111e3 * 1.2, 500);
|
|
2279
|
+
this.viewer.camera.flyTo({
|
|
2280
|
+
destination: C.Cartesian3.fromDegrees(centerLon, centerLat, cameraHeight),
|
|
2281
|
+
orientation: {
|
|
2282
|
+
heading: C.Math.toRadians(0),
|
|
2283
|
+
pitch: C.Math.toRadians(-90),
|
|
2284
|
+
roll: 0
|
|
2285
|
+
},
|
|
2286
|
+
duration
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
2290
|
+
|
|
2291
|
+
// src/core/tileset-manager/TilesetManager.ts
|
|
2292
|
+
var TilesetManager = class {
|
|
2293
|
+
constructor(CesiumNS, viewer) {
|
|
2294
|
+
this.CesiumNS = CesiumNS;
|
|
2295
|
+
this.viewer = viewer;
|
|
2296
|
+
/** 已加载的 Tileset */
|
|
2297
|
+
__publicField(this, "tilesets", /* @__PURE__ */ new Map());
|
|
2298
|
+
/** 事件发射器 */
|
|
2299
|
+
__publicField(this, "events", new Emitter());
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* 添加 3D Tiles
|
|
2303
|
+
* @param config Tileset 配置
|
|
2304
|
+
* @param options 加载选项
|
|
2305
|
+
*/
|
|
2306
|
+
async add(config, options) {
|
|
2307
|
+
if (this.tilesets.has(config.id)) {
|
|
2308
|
+
console.log(`[TilesetManager] Tileset "${config.id}" \u5DF2\u5B58\u5728\uFF0C\u8FD4\u56DE\u7F13\u5B58`);
|
|
2309
|
+
return { id: config.id, success: true };
|
|
2310
|
+
}
|
|
2311
|
+
try {
|
|
2312
|
+
const tileset = await this.createTileset(config);
|
|
2313
|
+
if (!tileset) {
|
|
2314
|
+
throw new Error("\u65E0\u6CD5\u521B\u5EFA Tileset");
|
|
2315
|
+
}
|
|
2316
|
+
this.viewer.scene.primitives.add(tileset);
|
|
2317
|
+
tileset._customId = config.id;
|
|
2318
|
+
tileset._url = config.url;
|
|
2319
|
+
this.tilesets.set(config.id, { config, tileset });
|
|
2320
|
+
this.events.emit({ added: { config } });
|
|
2321
|
+
console.log(`[TilesetManager] \u5DF2\u6DFB\u52A0 Tileset: ${config.id}`);
|
|
2322
|
+
if (options?.flyTo) {
|
|
2323
|
+
await this.flyToTileset(tileset, options.flyDuration);
|
|
2324
|
+
}
|
|
2325
|
+
return { id: config.id, success: true };
|
|
2326
|
+
} catch (error) {
|
|
2327
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
2328
|
+
console.error(`[TilesetManager] \u6DFB\u52A0 Tileset \u5931\u8D25:`, error);
|
|
2329
|
+
this.events.emit({ error: { id: config.id, error } });
|
|
2330
|
+
return { id: config.id, success: false, error: errMsg };
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* 移除 Tileset
|
|
2335
|
+
* @param tilesetId Tileset ID
|
|
2336
|
+
*/
|
|
2337
|
+
remove(tilesetId) {
|
|
2338
|
+
const entry = this.tilesets.get(tilesetId);
|
|
2339
|
+
if (!entry) {
|
|
2340
|
+
return false;
|
|
2341
|
+
}
|
|
2342
|
+
this.viewer.scene.primitives.remove(entry.tileset);
|
|
2343
|
+
this.tilesets.delete(tilesetId);
|
|
2344
|
+
this.events.emit({ removed: { id: tilesetId } });
|
|
2345
|
+
console.log(`[TilesetManager] \u5DF2\u79FB\u9664 Tileset: ${tilesetId}`);
|
|
2346
|
+
return true;
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* 设置 Tileset 可见性
|
|
2350
|
+
* @param tilesetId Tileset ID
|
|
2351
|
+
* @param visible 是否可见
|
|
2352
|
+
*/
|
|
2353
|
+
setVisible(tilesetId, visible) {
|
|
2354
|
+
const entry = this.tilesets.get(tilesetId);
|
|
2355
|
+
if (!entry) {
|
|
2356
|
+
return false;
|
|
2357
|
+
}
|
|
2358
|
+
entry.tileset.show = visible;
|
|
2359
|
+
entry.config.show = visible;
|
|
2360
|
+
this.events.emit({ visibilityChanged: { id: tilesetId, visible } });
|
|
2361
|
+
return true;
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* 获取 Tileset 配置
|
|
2365
|
+
* @param tilesetId Tileset ID
|
|
2366
|
+
*/
|
|
2367
|
+
get(tilesetId) {
|
|
2368
|
+
return this.tilesets.get(tilesetId)?.config;
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* 获取底层 Cesium Tileset
|
|
2372
|
+
* @param tilesetId Tileset ID
|
|
2373
|
+
*/
|
|
2374
|
+
getTileset(tilesetId) {
|
|
2375
|
+
return this.tilesets.get(tilesetId)?.tileset;
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* 获取所有 Tileset 配置
|
|
2379
|
+
*/
|
|
2380
|
+
list() {
|
|
2381
|
+
return Array.from(this.tilesets.values()).map((e) => e.config);
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* 检查 Tileset 是否存在
|
|
2385
|
+
* @param tilesetId Tileset ID
|
|
2386
|
+
*/
|
|
2387
|
+
has(tilesetId) {
|
|
2388
|
+
return this.tilesets.has(tilesetId);
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* 飞行到指定 Tileset
|
|
2392
|
+
* @param tilesetId Tileset ID
|
|
2393
|
+
* @param duration 飞行时长
|
|
2394
|
+
*/
|
|
2395
|
+
async flyTo(tilesetId, duration) {
|
|
2396
|
+
const entry = this.tilesets.get(tilesetId);
|
|
2397
|
+
if (!entry) {
|
|
2398
|
+
return false;
|
|
2399
|
+
}
|
|
2400
|
+
await this.flyToTileset(entry.tileset, duration);
|
|
2401
|
+
return true;
|
|
2402
|
+
}
|
|
2403
|
+
/**
|
|
2404
|
+
* 清除所有 Tileset
|
|
2405
|
+
*/
|
|
2406
|
+
clear() {
|
|
2407
|
+
const ids = Array.from(this.tilesets.keys());
|
|
2408
|
+
ids.forEach((id) => this.remove(id));
|
|
2409
|
+
console.log(`[TilesetManager] \u5DF2\u6E05\u9664\u6240\u6709 Tileset`);
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* 销毁管理器
|
|
2413
|
+
*/
|
|
2414
|
+
destroy() {
|
|
2415
|
+
this.clear();
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* 创建 Tileset
|
|
2419
|
+
*/
|
|
2420
|
+
async createTileset(config) {
|
|
2421
|
+
const C = this.CesiumNS;
|
|
2422
|
+
const pointCloudShading = config.pointCloudShading ?? {
|
|
2423
|
+
attenuation: true,
|
|
2424
|
+
geometricErrorScale: 1,
|
|
2425
|
+
eyeDomeLighting: true,
|
|
2426
|
+
eyeDomeLightingStrength: 1,
|
|
2427
|
+
eyeDomeLightingRadius: 1
|
|
2428
|
+
};
|
|
2429
|
+
const tileset = await C.Cesium3DTileset.fromUrl(config.url, {
|
|
2430
|
+
show: config.show ?? true,
|
|
2431
|
+
maximumScreenSpaceError: config.maximumScreenSpaceError ?? 16,
|
|
2432
|
+
dynamicScreenSpaceError: true,
|
|
2433
|
+
dynamicScreenSpaceErrorDensity: 278e-5,
|
|
2434
|
+
dynamicScreenSpaceErrorFactor: 4,
|
|
2435
|
+
skipLevelOfDetail: false,
|
|
2436
|
+
baseScreenSpaceError: 1024,
|
|
2437
|
+
skipScreenSpaceErrorFactor: 16,
|
|
2438
|
+
skipLevels: 1,
|
|
2439
|
+
immediatelyLoadDesiredLevelOfDetail: false,
|
|
2440
|
+
loadSiblings: false,
|
|
2441
|
+
cullWithChildrenBounds: true,
|
|
2442
|
+
pointCloudShading
|
|
2443
|
+
});
|
|
2444
|
+
return tileset;
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* 飞行到 Tileset
|
|
2448
|
+
*/
|
|
2449
|
+
async flyToTileset(tileset, duration = 2) {
|
|
2450
|
+
try {
|
|
2451
|
+
await this.viewer.flyTo(tileset, { duration });
|
|
2452
|
+
} catch {
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2456
|
+
|
|
1698
2457
|
// src/core/SceneManager.ts
|
|
1699
2458
|
var SceneManager = class {
|
|
1700
2459
|
constructor(CesiumNS, options) {
|
|
@@ -1702,6 +2461,9 @@ var SceneManager = class {
|
|
|
1702
2461
|
__publicField(this, "viewer");
|
|
1703
2462
|
__publicField(this, "layerManager");
|
|
1704
2463
|
__publicField(this, "cameraManager");
|
|
2464
|
+
__publicField(this, "terrainManager");
|
|
2465
|
+
__publicField(this, "imageryManager");
|
|
2466
|
+
__publicField(this, "tilesetManager");
|
|
1705
2467
|
console.log("SceneManager constructor", options);
|
|
1706
2468
|
assertCesiumAssetsConfigured();
|
|
1707
2469
|
ensureCesiumIonToken(CesiumNS);
|
|
@@ -1709,10 +2471,8 @@ var SceneManager = class {
|
|
|
1709
2471
|
const container = this.resolveContainer(options.container);
|
|
1710
2472
|
this.viewer = new CesiumNS.Viewer(container, {
|
|
1711
2473
|
baseLayer: false,
|
|
1712
|
-
// 时间控制相关
|
|
1713
2474
|
animation: false,
|
|
1714
2475
|
timeline: false,
|
|
1715
|
-
// UI控件
|
|
1716
2476
|
geocoder: false,
|
|
1717
2477
|
homeButton: false,
|
|
1718
2478
|
sceneModePicker: false,
|
|
@@ -1720,15 +2480,11 @@ var SceneManager = class {
|
|
|
1720
2480
|
navigationHelpButton: false,
|
|
1721
2481
|
fullscreenButton: false,
|
|
1722
2482
|
vrButton: false,
|
|
1723
|
-
// 信息框
|
|
1724
2483
|
infoBox: false,
|
|
1725
2484
|
selectionIndicator: false,
|
|
1726
|
-
// 默认数据源
|
|
1727
2485
|
shouldAnimate: false,
|
|
1728
|
-
// 地形和影像
|
|
1729
2486
|
terrainProvider: void 0,
|
|
1730
2487
|
creditContainer: void 0,
|
|
1731
|
-
// 其他
|
|
1732
2488
|
contextOptions: {
|
|
1733
2489
|
webgl: {
|
|
1734
2490
|
alpha: true,
|
|
@@ -1737,7 +2493,6 @@ var SceneManager = class {
|
|
|
1737
2493
|
antialias: true,
|
|
1738
2494
|
powerPreference: "high-performance",
|
|
1739
2495
|
preserveDrawingBuffer: true
|
|
1740
|
-
// 必需:允许 canvas.toDataURL() 截图
|
|
1741
2496
|
}
|
|
1742
2497
|
},
|
|
1743
2498
|
...viewerOptions
|
|
@@ -1749,6 +2504,9 @@ var SceneManager = class {
|
|
|
1749
2504
|
}
|
|
1750
2505
|
});
|
|
1751
2506
|
this.cameraManager = new CameraManager(CesiumNS, this.viewer);
|
|
2507
|
+
this.terrainManager = new TerrainManager(CesiumNS, this.viewer);
|
|
2508
|
+
this.imageryManager = new ImageryManager(CesiumNS, this.viewer);
|
|
2509
|
+
this.tilesetManager = new TilesetManager(CesiumNS, this.viewer);
|
|
1752
2510
|
if (imagery) this.applyImagery(imagery);
|
|
1753
2511
|
if (terrain) this.applyTerrain(terrain);
|
|
1754
2512
|
if (typeof shadows === "boolean") this.setShadows(shadows);
|
|
@@ -1759,7 +2517,7 @@ var SceneManager = class {
|
|
|
1759
2517
|
return this.viewer;
|
|
1760
2518
|
}
|
|
1761
2519
|
/**
|
|
1762
|
-
* Set a black background for areas without imagery.
|
|
2520
|
+
* Set a black background for areas without imagery.
|
|
1763
2521
|
*/
|
|
1764
2522
|
useBlackBackground(removeAllImagery = true) {
|
|
1765
2523
|
const C = this.CesiumNS;
|
|
@@ -1767,6 +2525,9 @@ var SceneManager = class {
|
|
|
1767
2525
|
this.viewer.scene.globe.baseColor = C.Color.BLACK;
|
|
1768
2526
|
}
|
|
1769
2527
|
destroy() {
|
|
2528
|
+
this.terrainManager?.destroy();
|
|
2529
|
+
this.imageryManager?.destroy();
|
|
2530
|
+
this.tilesetManager?.destroy();
|
|
1770
2531
|
this.viewer?.destroy();
|
|
1771
2532
|
this.viewer = void 0;
|
|
1772
2533
|
}
|
|
@@ -1791,6 +2552,18 @@ var SceneManager = class {
|
|
|
1791
2552
|
getLayerManager() {
|
|
1792
2553
|
return this.layerManager;
|
|
1793
2554
|
}
|
|
2555
|
+
getTerrainManager() {
|
|
2556
|
+
return this.terrainManager;
|
|
2557
|
+
}
|
|
2558
|
+
getImageryManager() {
|
|
2559
|
+
return this.imageryManager;
|
|
2560
|
+
}
|
|
2561
|
+
getTilesetManager() {
|
|
2562
|
+
return this.tilesetManager;
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* @deprecated 请使用 getImageryManager().add() 代替
|
|
2566
|
+
*/
|
|
1794
2567
|
setBaseImagery(url, layer_id, opts) {
|
|
1795
2568
|
for (let i = 0; i < this.viewer.imageryLayers.length; i++) {
|
|
1796
2569
|
const layer2 = this.viewer.imageryLayers.get(i);
|
|
@@ -1813,7 +2586,9 @@ var SceneManager = class {
|
|
|
1813
2586
|
}
|
|
1814
2587
|
return layer;
|
|
1815
2588
|
}
|
|
1816
|
-
|
|
2589
|
+
/**
|
|
2590
|
+
* @deprecated 请使用 getTilesetManager().add() 代替
|
|
2591
|
+
*/
|
|
1817
2592
|
async set3DTiles(url, layer_id) {
|
|
1818
2593
|
const primitives = this.viewer.scene.primitives;
|
|
1819
2594
|
for (let i = primitives.length - 1; i >= 0; i--) {
|
|
@@ -1837,6 +2612,9 @@ var SceneManager = class {
|
|
|
1837
2612
|
return void 0;
|
|
1838
2613
|
}
|
|
1839
2614
|
}
|
|
2615
|
+
/**
|
|
2616
|
+
* @deprecated 请使用 getTerrainManager().switchTo() 代替
|
|
2617
|
+
*/
|
|
1840
2618
|
async setTerrain(options) {
|
|
1841
2619
|
if (this.viewer.scene.skyBox) {
|
|
1842
2620
|
this.viewer.scene.skyBox.show = false;
|
|
@@ -1845,7 +2623,6 @@ var SceneManager = class {
|
|
|
1845
2623
|
this.viewer.scene.globe.baseColor = this.CesiumNS.Color.BLACK;
|
|
1846
2624
|
return this.applyTerrain(options);
|
|
1847
2625
|
}
|
|
1848
|
-
// Expose DSM (3D Tiles) loading for demos and consumers
|
|
1849
2626
|
async addDSM(options) {
|
|
1850
2627
|
const handle = await this.layerManager.addDSM(options);
|
|
1851
2628
|
try {
|
|
@@ -1874,8 +2651,7 @@ var SceneManager = class {
|
|
|
1874
2651
|
try {
|
|
1875
2652
|
const handle = this.layerManager.addDOM(options);
|
|
1876
2653
|
if (!handle) return void 0;
|
|
1877
|
-
|
|
1878
|
-
return layer;
|
|
2654
|
+
return this.layerManager.getImageryLayer(handle.id);
|
|
1879
2655
|
} catch (e) {
|
|
1880
2656
|
console.error("[SceneManager] Failed to apply imagery:", e);
|
|
1881
2657
|
return void 0;
|
|
@@ -1884,7 +2660,6 @@ var SceneManager = class {
|
|
|
1884
2660
|
async apply3Dtiles(url) {
|
|
1885
2661
|
const tileset = await this.CesiumNS.Cesium3DTileset.fromUrl(url, {
|
|
1886
2662
|
maximumScreenSpaceError: 16,
|
|
1887
|
-
// 降低以提高点云质量
|
|
1888
2663
|
dynamicScreenSpaceError: true,
|
|
1889
2664
|
dynamicScreenSpaceErrorDensity: 278e-5,
|
|
1890
2665
|
dynamicScreenSpaceErrorFactor: 4,
|
|
@@ -1895,14 +2670,12 @@ var SceneManager = class {
|
|
|
1895
2670
|
immediatelyLoadDesiredLevelOfDetail: false,
|
|
1896
2671
|
loadSiblings: false,
|
|
1897
2672
|
cullWithChildrenBounds: true,
|
|
1898
|
-
// 点云特定选项
|
|
1899
2673
|
pointCloudShading: {
|
|
1900
2674
|
attenuation: true,
|
|
1901
2675
|
geometricErrorScale: 1,
|
|
1902
2676
|
maximumAttenuation: void 0,
|
|
1903
2677
|
baseResolution: void 0,
|
|
1904
2678
|
eyeDomeLighting: true,
|
|
1905
|
-
// 启用 EDL 可以改善点云视觉效果
|
|
1906
2679
|
eyeDomeLightingStrength: 1,
|
|
1907
2680
|
eyeDomeLightingRadius: 1
|
|
1908
2681
|
}
|
|
@@ -1912,7 +2685,6 @@ var SceneManager = class {
|
|
|
1912
2685
|
async applyTerrain(options) {
|
|
1913
2686
|
try {
|
|
1914
2687
|
const handle = await this.layerManager.addDEM(options);
|
|
1915
|
-
console.log("handle", this.viewer.terrainProvider);
|
|
1916
2688
|
return handle ? this.viewer.terrainProvider : void 0;
|
|
1917
2689
|
} catch (e) {
|
|
1918
2690
|
console.error("[SceneManager] Failed to apply terrain:", e);
|
|
@@ -13337,6 +14109,7 @@ var MassPolygonManager = class {
|
|
|
13337
14109
|
// 事件处理器
|
|
13338
14110
|
__publicField(this, "hoverHandler");
|
|
13339
14111
|
__publicField(this, "clickHandler");
|
|
14112
|
+
__publicField(this, "rightClickHandler");
|
|
13340
14113
|
// 悬停状态
|
|
13341
14114
|
__publicField(this, "highlightedId");
|
|
13342
14115
|
__publicField(this, "originalHighlightFillColor");
|
|
@@ -13345,11 +14118,36 @@ var MassPolygonManager = class {
|
|
|
13345
14118
|
__publicField(this, "selectedId");
|
|
13346
14119
|
__publicField(this, "originalSelectFillColor");
|
|
13347
14120
|
__publicField(this, "originalSelectOutlineColor");
|
|
14121
|
+
// 贴地模式高亮用的独立 Primitive
|
|
14122
|
+
__publicField(this, "highlightPrimitive");
|
|
14123
|
+
__publicField(this, "selectPrimitive");
|
|
14124
|
+
// 贴地模式轮廓线
|
|
14125
|
+
__publicField(this, "groundOutlinePrimitive");
|
|
13348
14126
|
// 节流相关
|
|
13349
14127
|
__publicField(this, "lastPickTime", 0);
|
|
13350
14128
|
__publicField(this, "pickThrottleMs", 50);
|
|
13351
14129
|
// 悬停标签
|
|
13352
14130
|
__publicField(this, "hoverLabel");
|
|
14131
|
+
// 静态标签(每个多边形的名称标签)
|
|
14132
|
+
__publicField(this, "staticLabelCollection");
|
|
14133
|
+
__publicField(this, "staticLabelsVisible", false);
|
|
14134
|
+
__publicField(this, "staticLabelStyle", {
|
|
14135
|
+
font: "12px sans-serif",
|
|
14136
|
+
fillColor: "#000000",
|
|
14137
|
+
// 黑色字
|
|
14138
|
+
outlineColor: "#ffffff",
|
|
14139
|
+
// 白色描边
|
|
14140
|
+
outlineWidth: 2,
|
|
14141
|
+
showBackground: false,
|
|
14142
|
+
backgroundColor: "rgba(0,0,0,0.5)",
|
|
14143
|
+
backgroundPadding: [4, 2],
|
|
14144
|
+
pixelOffset: [0, 0],
|
|
14145
|
+
scale: 1
|
|
14146
|
+
});
|
|
14147
|
+
// 地形高度缓存(用于 hover 标签等需要快速访问地形高度的场景)
|
|
14148
|
+
__publicField(this, "terrainHeightCache", /* @__PURE__ */ new Map());
|
|
14149
|
+
// 销毁标志
|
|
14150
|
+
__publicField(this, "isDestroyed", false);
|
|
13353
14151
|
// ========== 编辑支持方法 ==========
|
|
13354
14152
|
// 隐藏的多边形原始颜色存储
|
|
13355
14153
|
__publicField(this, "hiddenPolygonColors", /* @__PURE__ */ new Map());
|
|
@@ -13363,6 +14161,7 @@ var MassPolygonManager = class {
|
|
|
13363
14161
|
this.interactionOptions = {
|
|
13364
14162
|
enableHover: false,
|
|
13365
14163
|
enableClick: false,
|
|
14164
|
+
enableRightClick: false,
|
|
13366
14165
|
highlightStyle: {
|
|
13367
14166
|
fillColor: "rgba(255, 255, 0, 0.7)",
|
|
13368
14167
|
outlineColor: "rgba(255, 255, 0, 1)"
|
|
@@ -13382,7 +14181,8 @@ var MassPolygonManager = class {
|
|
|
13382
14181
|
pixelOffset: [0, -20]
|
|
13383
14182
|
},
|
|
13384
14183
|
onHover: void 0,
|
|
13385
|
-
onClick: void 0
|
|
14184
|
+
onClick: void 0,
|
|
14185
|
+
onRightClick: void 0
|
|
13386
14186
|
};
|
|
13387
14187
|
}
|
|
13388
14188
|
/**
|
|
@@ -13414,6 +14214,7 @@ var MassPolygonManager = class {
|
|
|
13414
14214
|
this.interactionOptions = {
|
|
13415
14215
|
enableHover: interaction.enableHover ?? false,
|
|
13416
14216
|
enableClick: interaction.enableClick ?? false,
|
|
14217
|
+
enableRightClick: interaction.enableRightClick ?? false,
|
|
13417
14218
|
highlightStyle: {
|
|
13418
14219
|
fillColor: interaction.highlightStyle?.fillColor ?? this.interactionOptions.highlightStyle.fillColor,
|
|
13419
14220
|
outlineColor: interaction.highlightStyle?.outlineColor ?? this.interactionOptions.highlightStyle.outlineColor
|
|
@@ -13433,12 +14234,27 @@ var MassPolygonManager = class {
|
|
|
13433
14234
|
pixelOffset: interaction.hoverLabelStyle?.pixelOffset ?? this.interactionOptions.hoverLabelStyle.pixelOffset
|
|
13434
14235
|
},
|
|
13435
14236
|
onHover: interaction.onHover,
|
|
13436
|
-
onClick: interaction.onClick
|
|
14237
|
+
onClick: interaction.onClick,
|
|
14238
|
+
onRightClick: interaction.onRightClick
|
|
13437
14239
|
};
|
|
13438
14240
|
}
|
|
13439
14241
|
this.isClampToGround = options?.clampToGround ?? false;
|
|
13440
14242
|
const asynchronous = options?.asynchronous ?? true;
|
|
13441
14243
|
this.polygonHeight = options?.height ?? 0;
|
|
14244
|
+
if (options?.staticLabelStyle) {
|
|
14245
|
+
const labelStyle = options.staticLabelStyle;
|
|
14246
|
+
this.staticLabelStyle = {
|
|
14247
|
+
font: labelStyle.font ?? this.staticLabelStyle.font,
|
|
14248
|
+
fillColor: labelStyle.fillColor ?? this.staticLabelStyle.fillColor,
|
|
14249
|
+
outlineColor: labelStyle.outlineColor ?? this.staticLabelStyle.outlineColor,
|
|
14250
|
+
outlineWidth: labelStyle.outlineWidth ?? this.staticLabelStyle.outlineWidth,
|
|
14251
|
+
showBackground: labelStyle.showBackground ?? this.staticLabelStyle.showBackground,
|
|
14252
|
+
backgroundColor: labelStyle.backgroundColor ?? this.staticLabelStyle.backgroundColor,
|
|
14253
|
+
backgroundPadding: labelStyle.backgroundPadding ?? this.staticLabelStyle.backgroundPadding,
|
|
14254
|
+
pixelOffset: labelStyle.pixelOffset ?? this.staticLabelStyle.pixelOffset,
|
|
14255
|
+
scale: labelStyle.scale ?? this.staticLabelStyle.scale
|
|
14256
|
+
};
|
|
14257
|
+
}
|
|
13442
14258
|
if (this.isClampToGround && this.viewer.scene.globe) {
|
|
13443
14259
|
this.viewer.scene.globe.depthTestAgainstTerrain = true;
|
|
13444
14260
|
}
|
|
@@ -13458,55 +14274,112 @@ var MassPolygonManager = class {
|
|
|
13458
14274
|
continue;
|
|
13459
14275
|
}
|
|
13460
14276
|
const positions = this.convertPointsToCartesian(polygon.points);
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
14277
|
+
if (this.isClampToGround) {
|
|
14278
|
+
const polygonGeometry = new C.PolygonGeometry({
|
|
14279
|
+
polygonHierarchy: new C.PolygonHierarchy(positions)
|
|
14280
|
+
});
|
|
14281
|
+
fillInstances.push(new C.GeometryInstance({
|
|
14282
|
+
geometry: polygonGeometry,
|
|
14283
|
+
id: polygon.id,
|
|
14284
|
+
attributes: {
|
|
14285
|
+
color: C.ColorGeometryInstanceAttribute.fromColor(fillColor)
|
|
14286
|
+
}
|
|
14287
|
+
}));
|
|
14288
|
+
} else {
|
|
14289
|
+
const polygonGeometry = new C.PolygonGeometry({
|
|
14290
|
+
polygonHierarchy: new C.PolygonHierarchy(positions),
|
|
14291
|
+
perPositionHeight: false,
|
|
14292
|
+
height: this.polygonHeight
|
|
14293
|
+
});
|
|
14294
|
+
fillInstances.push(new C.GeometryInstance({
|
|
14295
|
+
geometry: polygonGeometry,
|
|
14296
|
+
id: polygon.id,
|
|
14297
|
+
attributes: {
|
|
14298
|
+
color: C.ColorGeometryInstanceAttribute.fromColor(fillColor),
|
|
14299
|
+
show: new C.ShowGeometryInstanceAttribute(true)
|
|
14300
|
+
}
|
|
14301
|
+
}));
|
|
14302
|
+
}
|
|
14303
|
+
const outlinePositions = polygon.points.map(
|
|
14304
|
+
(p) => C.Cartesian3.fromDegrees(p.lon, p.lat, this.isClampToGround ? 0 : this.polygonHeight)
|
|
13477
14305
|
);
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
14306
|
+
outlinePositions.push(outlinePositions[0]);
|
|
14307
|
+
if (this.isClampToGround) {
|
|
14308
|
+
const groundOutlineGeometry = new C.GroundPolylineGeometry({
|
|
14309
|
+
positions: outlinePositions,
|
|
14310
|
+
width: this.currentStyle.outlineWidth
|
|
14311
|
+
});
|
|
14312
|
+
outlineInstances.push(new C.GeometryInstance({
|
|
14313
|
+
geometry: groundOutlineGeometry,
|
|
14314
|
+
id: `${polygon.id}-outline`,
|
|
14315
|
+
attributes: {
|
|
14316
|
+
color: C.ColorGeometryInstanceAttribute.fromColor(outlineColor)
|
|
14317
|
+
}
|
|
14318
|
+
}));
|
|
14319
|
+
} else {
|
|
14320
|
+
const outlineGeometry = new C.PolylineGeometry({
|
|
14321
|
+
positions: outlinePositions,
|
|
14322
|
+
width: this.currentStyle.outlineWidth
|
|
14323
|
+
});
|
|
14324
|
+
outlineInstances.push(new C.GeometryInstance({
|
|
14325
|
+
geometry: outlineGeometry,
|
|
14326
|
+
id: `${polygon.id}-outline`,
|
|
14327
|
+
attributes: {
|
|
14328
|
+
color: C.ColorGeometryInstanceAttribute.fromColor(outlineColor),
|
|
14329
|
+
show: new C.ShowGeometryInstanceAttribute(true)
|
|
14330
|
+
}
|
|
14331
|
+
}));
|
|
14332
|
+
}
|
|
13491
14333
|
}
|
|
13492
14334
|
if (fillInstances.length > 0) {
|
|
13493
14335
|
const appearance = new C.PerInstanceColorAppearance({ flat: true, translucent: true });
|
|
13494
|
-
this.
|
|
14336
|
+
if (this.isClampToGround) {
|
|
14337
|
+
this.primitive = new C.GroundPrimitive({
|
|
14338
|
+
geometryInstances: fillInstances,
|
|
14339
|
+
appearance,
|
|
14340
|
+
asynchronous,
|
|
14341
|
+
releaseGeometryInstances: false,
|
|
14342
|
+
allowPicking: true
|
|
14343
|
+
});
|
|
14344
|
+
} else {
|
|
14345
|
+
this.primitive = new C.Primitive({
|
|
14346
|
+
geometryInstances: fillInstances,
|
|
14347
|
+
appearance,
|
|
14348
|
+
asynchronous,
|
|
14349
|
+
releaseGeometryInstances: false,
|
|
14350
|
+
allowPicking: true
|
|
14351
|
+
});
|
|
14352
|
+
}
|
|
13495
14353
|
this.layerCollection.add(this.primitive);
|
|
13496
14354
|
}
|
|
13497
14355
|
if (outlineInstances.length > 0) {
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
14356
|
+
if (this.isClampToGround) {
|
|
14357
|
+
const appearance = new C.PolylineColorAppearance();
|
|
14358
|
+
this.groundOutlinePrimitive = new C.GroundPolylinePrimitive({
|
|
14359
|
+
geometryInstances: outlineInstances,
|
|
14360
|
+
appearance,
|
|
14361
|
+
asynchronous
|
|
14362
|
+
});
|
|
14363
|
+
this.layerCollection.add(this.groundOutlinePrimitive);
|
|
14364
|
+
} else {
|
|
14365
|
+
const appearance = new C.PolylineColorAppearance();
|
|
14366
|
+
this.outlinePrimitive = new C.Primitive({ geometryInstances: outlineInstances, appearance, asynchronous });
|
|
14367
|
+
this.layerCollection.add(this.outlinePrimitive);
|
|
14368
|
+
}
|
|
13501
14369
|
}
|
|
13502
|
-
this.labelCollection = new C.LabelCollection();
|
|
14370
|
+
this.labelCollection = new C.LabelCollection({ scene: this.viewer.scene });
|
|
13503
14371
|
this.layerCollection.add(this.labelCollection);
|
|
14372
|
+
this.staticLabelCollection = new C.LabelCollection({ scene: this.viewer.scene });
|
|
14373
|
+
this.layerCollection.add(this.staticLabelCollection);
|
|
13504
14374
|
if (this.interactionOptions.enableHover) {
|
|
13505
14375
|
this.setupHoverHandler();
|
|
13506
14376
|
}
|
|
13507
14377
|
if (this.interactionOptions.enableClick) {
|
|
13508
14378
|
this.setupClickHandler();
|
|
13509
14379
|
}
|
|
14380
|
+
if (this.interactionOptions.enableRightClick) {
|
|
14381
|
+
this.setupRightClickHandler();
|
|
14382
|
+
}
|
|
13510
14383
|
this.viewer.scene.requestRender();
|
|
13511
14384
|
return {
|
|
13512
14385
|
primitive: this.primitive,
|
|
@@ -13527,19 +14400,25 @@ var MassPolygonManager = class {
|
|
|
13527
14400
|
const now = Date.now();
|
|
13528
14401
|
if (now - this.lastPickTime < this.pickThrottleMs) return;
|
|
13529
14402
|
this.lastPickTime = now;
|
|
13530
|
-
|
|
13531
|
-
if (
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
this.interactionOptions.onHover?.(polygonId, data ?? null);
|
|
14403
|
+
let polygonId = null;
|
|
14404
|
+
if (this.isClampToGround) {
|
|
14405
|
+
polygonId = this.pickPolygonByPosition(movement.endPosition);
|
|
14406
|
+
} else {
|
|
14407
|
+
const pickedObject = this.viewer.scene.pick(movement.endPosition);
|
|
14408
|
+
if (pickedObject?.id && typeof pickedObject.id === "string") {
|
|
14409
|
+
const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
|
|
14410
|
+
if (this.polygonDataMap.has(pickedId)) {
|
|
14411
|
+
polygonId = pickedId;
|
|
13540
14412
|
}
|
|
13541
|
-
}
|
|
13542
|
-
|
|
14413
|
+
}
|
|
14414
|
+
}
|
|
14415
|
+
if (polygonId) {
|
|
14416
|
+
if (this.highlightedId !== polygonId) {
|
|
14417
|
+
this.clearHighlight();
|
|
14418
|
+
this.highlightPolygon(polygonId);
|
|
14419
|
+
const data = this.polygonDataMap.get(polygonId);
|
|
14420
|
+
if (data) this.showLabel(data);
|
|
14421
|
+
this.interactionOptions.onHover?.(polygonId, data ?? null);
|
|
13543
14422
|
}
|
|
13544
14423
|
} else {
|
|
13545
14424
|
this.handleMouseLeave();
|
|
@@ -13559,23 +14438,26 @@ var MassPolygonManager = class {
|
|
|
13559
14438
|
this.clickHandler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
|
|
13560
14439
|
this.clickHandler.setInputAction(
|
|
13561
14440
|
(movement) => {
|
|
13562
|
-
|
|
13563
|
-
if (
|
|
13564
|
-
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
const data = this.polygonDataMap.get(polygonId);
|
|
13572
|
-
this.interactionOptions.onClick?.(polygonId, data ?? null);
|
|
14441
|
+
let polygonId = null;
|
|
14442
|
+
if (this.isClampToGround) {
|
|
14443
|
+
polygonId = this.pickPolygonByPosition(movement.position);
|
|
14444
|
+
} else {
|
|
14445
|
+
const pickedObject = this.viewer.scene.pick(movement.position);
|
|
14446
|
+
if (pickedObject?.id && typeof pickedObject.id === "string") {
|
|
14447
|
+
const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
|
|
14448
|
+
if (this.polygonDataMap.has(pickedId)) {
|
|
14449
|
+
polygonId = pickedId;
|
|
13573
14450
|
}
|
|
14451
|
+
}
|
|
14452
|
+
}
|
|
14453
|
+
if (polygonId) {
|
|
14454
|
+
if (this.selectedId === polygonId) {
|
|
14455
|
+
this.deselect();
|
|
14456
|
+
this.interactionOptions.onClick?.(null, null);
|
|
13574
14457
|
} else {
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
}
|
|
14458
|
+
this.select(polygonId);
|
|
14459
|
+
const data = this.polygonDataMap.get(polygonId);
|
|
14460
|
+
this.interactionOptions.onClick?.(polygonId, data ?? null);
|
|
13579
14461
|
}
|
|
13580
14462
|
} else {
|
|
13581
14463
|
if (this.selectedId) {
|
|
@@ -13587,6 +14469,79 @@ var MassPolygonManager = class {
|
|
|
13587
14469
|
C.ScreenSpaceEventType.LEFT_CLICK
|
|
13588
14470
|
);
|
|
13589
14471
|
}
|
|
14472
|
+
/**
|
|
14473
|
+
* 设置右键事件处理器
|
|
14474
|
+
*/
|
|
14475
|
+
setupRightClickHandler() {
|
|
14476
|
+
if (this.rightClickHandler) {
|
|
14477
|
+
this.rightClickHandler.destroy();
|
|
14478
|
+
}
|
|
14479
|
+
const C = this.CesiumNS;
|
|
14480
|
+
this.rightClickHandler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
|
|
14481
|
+
this.rightClickHandler.setInputAction(
|
|
14482
|
+
(movement) => {
|
|
14483
|
+
let polygonId = null;
|
|
14484
|
+
if (this.isClampToGround) {
|
|
14485
|
+
polygonId = this.pickPolygonByPosition(movement.position);
|
|
14486
|
+
} else {
|
|
14487
|
+
const pickedObject = this.viewer.scene.pick(movement.position);
|
|
14488
|
+
if (pickedObject?.id && typeof pickedObject.id === "string") {
|
|
14489
|
+
const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
|
|
14490
|
+
if (this.polygonDataMap.has(pickedId)) {
|
|
14491
|
+
polygonId = pickedId;
|
|
14492
|
+
}
|
|
14493
|
+
}
|
|
14494
|
+
}
|
|
14495
|
+
if (polygonId) {
|
|
14496
|
+
this.select(polygonId);
|
|
14497
|
+
const data = this.polygonDataMap.get(polygonId);
|
|
14498
|
+
if (data) {
|
|
14499
|
+
const canvas = this.viewer.scene.canvas;
|
|
14500
|
+
const canvasRect = canvas.getBoundingClientRect();
|
|
14501
|
+
const screenX = canvasRect.left + movement.position.x;
|
|
14502
|
+
const screenY = canvasRect.top + movement.position.y;
|
|
14503
|
+
this.interactionOptions.onRightClick?.(polygonId, data, { x: screenX, y: screenY });
|
|
14504
|
+
}
|
|
14505
|
+
}
|
|
14506
|
+
},
|
|
14507
|
+
C.ScreenSpaceEventType.RIGHT_CLICK
|
|
14508
|
+
);
|
|
14509
|
+
}
|
|
14510
|
+
/**
|
|
14511
|
+
* 通过屏幕坐标获取地理位置,然后判断在哪个多边形内
|
|
14512
|
+
* 用于贴地模式下的精确拾取
|
|
14513
|
+
*/
|
|
14514
|
+
pickPolygonByPosition(screenPosition) {
|
|
14515
|
+
const C = this.CesiumNS;
|
|
14516
|
+
const ray = this.viewer.camera.getPickRay(screenPosition);
|
|
14517
|
+
if (!ray) return null;
|
|
14518
|
+
const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
|
|
14519
|
+
if (!cartesian) return null;
|
|
14520
|
+
const cartographic = C.Cartographic.fromCartesian(cartesian);
|
|
14521
|
+
const lon = C.Math.toDegrees(cartographic.longitude);
|
|
14522
|
+
const lat = C.Math.toDegrees(cartographic.latitude);
|
|
14523
|
+
for (const [id, polygon] of this.polygonDataMap) {
|
|
14524
|
+
if (this.isPointInPolygon(lon, lat, polygon.points)) {
|
|
14525
|
+
return id;
|
|
14526
|
+
}
|
|
14527
|
+
}
|
|
14528
|
+
return null;
|
|
14529
|
+
}
|
|
14530
|
+
/**
|
|
14531
|
+
* 判断点是否在多边形内(射线法)
|
|
14532
|
+
*/
|
|
14533
|
+
isPointInPolygon(lon, lat, points) {
|
|
14534
|
+
let inside = false;
|
|
14535
|
+
const n = points.length;
|
|
14536
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
14537
|
+
const xi = points[i].lon, yi = points[i].lat;
|
|
14538
|
+
const xj = points[j].lon, yj = points[j].lat;
|
|
14539
|
+
if (yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi) {
|
|
14540
|
+
inside = !inside;
|
|
14541
|
+
}
|
|
14542
|
+
}
|
|
14543
|
+
return inside;
|
|
14544
|
+
}
|
|
13590
14545
|
/**
|
|
13591
14546
|
* 处理鼠标离开
|
|
13592
14547
|
*/
|
|
@@ -13604,6 +14559,11 @@ var MassPolygonManager = class {
|
|
|
13604
14559
|
if (id === this.selectedId) return;
|
|
13605
14560
|
const C = this.CesiumNS;
|
|
13606
14561
|
if (!this.primitive) return;
|
|
14562
|
+
if (this.isClampToGround) {
|
|
14563
|
+
this.highlightPolygonWithSeparatePrimitive(id, "highlight");
|
|
14564
|
+
this.highlightedId = id;
|
|
14565
|
+
return;
|
|
14566
|
+
}
|
|
13607
14567
|
try {
|
|
13608
14568
|
const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(id);
|
|
13609
14569
|
if (fillAttributes?.color) {
|
|
@@ -13624,11 +14584,60 @@ var MassPolygonManager = class {
|
|
|
13624
14584
|
} catch (e) {
|
|
13625
14585
|
}
|
|
13626
14586
|
}
|
|
14587
|
+
/**
|
|
14588
|
+
* 贴地模式下使用独立 Primitive 高亮/选中多边形
|
|
14589
|
+
*/
|
|
14590
|
+
highlightPolygonWithSeparatePrimitive(id, type) {
|
|
14591
|
+
const C = this.CesiumNS;
|
|
14592
|
+
const polygonData = this.polygonDataMap.get(id);
|
|
14593
|
+
if (!polygonData) return;
|
|
14594
|
+
if (type === "highlight" && this.highlightPrimitive) {
|
|
14595
|
+
this.layerCollection?.remove(this.highlightPrimitive);
|
|
14596
|
+
this.highlightPrimitive = void 0;
|
|
14597
|
+
} else if (type === "select" && this.selectPrimitive) {
|
|
14598
|
+
this.layerCollection?.remove(this.selectPrimitive);
|
|
14599
|
+
this.selectPrimitive = void 0;
|
|
14600
|
+
}
|
|
14601
|
+
const positions = this.convertPointsToCartesian(polygonData.points);
|
|
14602
|
+
const color = type === "highlight" ? C.Color.fromCssColorString(this.interactionOptions.highlightStyle.fillColor) : C.Color.fromCssColorString(this.interactionOptions.selectStyle.fillColor);
|
|
14603
|
+
const geometry = new C.PolygonGeometry({
|
|
14604
|
+
polygonHierarchy: new C.PolygonHierarchy(positions)
|
|
14605
|
+
});
|
|
14606
|
+
const instance = new C.GeometryInstance({
|
|
14607
|
+
geometry,
|
|
14608
|
+
id: `${id}-${type}`,
|
|
14609
|
+
attributes: {
|
|
14610
|
+
color: C.ColorGeometryInstanceAttribute.fromColor(color)
|
|
14611
|
+
}
|
|
14612
|
+
});
|
|
14613
|
+
const primitive = new C.GroundPrimitive({
|
|
14614
|
+
geometryInstances: instance,
|
|
14615
|
+
appearance: new C.PerInstanceColorAppearance({ flat: true, translucent: true }),
|
|
14616
|
+
asynchronous: false
|
|
14617
|
+
// 同步创建,立即显示
|
|
14618
|
+
});
|
|
14619
|
+
if (type === "highlight") {
|
|
14620
|
+
this.highlightPrimitive = primitive;
|
|
14621
|
+
} else {
|
|
14622
|
+
this.selectPrimitive = primitive;
|
|
14623
|
+
}
|
|
14624
|
+
this.layerCollection?.add(primitive);
|
|
14625
|
+
this.viewer.scene.requestRender();
|
|
14626
|
+
}
|
|
13627
14627
|
/**
|
|
13628
14628
|
* 清除悬停高亮
|
|
13629
14629
|
*/
|
|
13630
14630
|
clearHighlight() {
|
|
13631
14631
|
if (!this.highlightedId) return;
|
|
14632
|
+
if (this.isClampToGround) {
|
|
14633
|
+
if (this.highlightPrimitive) {
|
|
14634
|
+
this.layerCollection?.remove(this.highlightPrimitive);
|
|
14635
|
+
this.highlightPrimitive = void 0;
|
|
14636
|
+
}
|
|
14637
|
+
this.highlightedId = void 0;
|
|
14638
|
+
this.viewer.scene.requestRender();
|
|
14639
|
+
return;
|
|
14640
|
+
}
|
|
13632
14641
|
try {
|
|
13633
14642
|
if (this.primitive && this.originalHighlightFillColor) {
|
|
13634
14643
|
const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(this.highlightedId);
|
|
@@ -13664,6 +14673,11 @@ var MassPolygonManager = class {
|
|
|
13664
14673
|
}
|
|
13665
14674
|
const C = this.CesiumNS;
|
|
13666
14675
|
if (!this.primitive) return false;
|
|
14676
|
+
if (this.isClampToGround) {
|
|
14677
|
+
this.highlightPolygonWithSeparatePrimitive(id, "select");
|
|
14678
|
+
this.selectedId = id;
|
|
14679
|
+
return true;
|
|
14680
|
+
}
|
|
13667
14681
|
try {
|
|
13668
14682
|
const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(id);
|
|
13669
14683
|
if (fillAttributes?.color) {
|
|
@@ -13691,6 +14705,15 @@ var MassPolygonManager = class {
|
|
|
13691
14705
|
*/
|
|
13692
14706
|
deselect() {
|
|
13693
14707
|
if (!this.selectedId) return;
|
|
14708
|
+
if (this.isClampToGround) {
|
|
14709
|
+
if (this.selectPrimitive) {
|
|
14710
|
+
this.layerCollection?.remove(this.selectPrimitive);
|
|
14711
|
+
this.selectPrimitive = void 0;
|
|
14712
|
+
}
|
|
14713
|
+
this.selectedId = void 0;
|
|
14714
|
+
this.viewer.scene.requestRender();
|
|
14715
|
+
return;
|
|
14716
|
+
}
|
|
13694
14717
|
try {
|
|
13695
14718
|
if (this.primitive && this.originalSelectFillColor) {
|
|
13696
14719
|
const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(this.selectedId);
|
|
@@ -13725,7 +14748,7 @@ var MassPolygonManager = class {
|
|
|
13725
14748
|
return this.selectedId ?? null;
|
|
13726
14749
|
}
|
|
13727
14750
|
/**
|
|
13728
|
-
*
|
|
14751
|
+
* 计算多边形中心点(简单平均)
|
|
13729
14752
|
*/
|
|
13730
14753
|
calculatePolygonCenter(points) {
|
|
13731
14754
|
let sumLon = 0, sumLat = 0, sumHeight = 0;
|
|
@@ -13740,8 +14763,45 @@ var MassPolygonManager = class {
|
|
|
13740
14763
|
height: sumHeight / points.length
|
|
13741
14764
|
};
|
|
13742
14765
|
}
|
|
14766
|
+
/**
|
|
14767
|
+
* 计算多边形质心(更准确的中心点算法)
|
|
14768
|
+
* 使用多边形质心公式,对于凹多边形也能得到正确的中心位置
|
|
14769
|
+
*/
|
|
14770
|
+
calculatePolygonCentroid(points) {
|
|
14771
|
+
if (points.length < 3) {
|
|
14772
|
+
return this.calculatePolygonCenter(points);
|
|
14773
|
+
}
|
|
14774
|
+
let signedArea = 0;
|
|
14775
|
+
let cx = 0;
|
|
14776
|
+
let cy = 0;
|
|
14777
|
+
let sumHeight = 0;
|
|
14778
|
+
const n = points.length;
|
|
14779
|
+
for (let i = 0; i < n; i++) {
|
|
14780
|
+
const x0 = points[i].lon;
|
|
14781
|
+
const y0 = points[i].lat;
|
|
14782
|
+
const x1 = points[(i + 1) % n].lon;
|
|
14783
|
+
const y1 = points[(i + 1) % n].lat;
|
|
14784
|
+
const a = x0 * y1 - x1 * y0;
|
|
14785
|
+
signedArea += a;
|
|
14786
|
+
cx += (x0 + x1) * a;
|
|
14787
|
+
cy += (y0 + y1) * a;
|
|
14788
|
+
sumHeight += points[i].height ?? 0;
|
|
14789
|
+
}
|
|
14790
|
+
signedArea *= 0.5;
|
|
14791
|
+
if (Math.abs(signedArea) < 1e-10) {
|
|
14792
|
+
return this.calculatePolygonCenter(points);
|
|
14793
|
+
}
|
|
14794
|
+
cx = cx / (6 * signedArea);
|
|
14795
|
+
cy = cy / (6 * signedArea);
|
|
14796
|
+
return {
|
|
14797
|
+
lon: cx,
|
|
14798
|
+
lat: cy,
|
|
14799
|
+
height: sumHeight / n
|
|
14800
|
+
};
|
|
14801
|
+
}
|
|
13743
14802
|
/**
|
|
13744
14803
|
* 显示悬停标签
|
|
14804
|
+
* 使用缓存的地形高度(如果可用)来正确放置标签
|
|
13745
14805
|
*/
|
|
13746
14806
|
showLabel(polygonData) {
|
|
13747
14807
|
if (!this.interactionOptions.showHoverLabel || !polygonData.name) return;
|
|
@@ -13750,7 +14810,24 @@ var MassPolygonManager = class {
|
|
|
13750
14810
|
const style = this.interactionOptions.hoverLabelStyle;
|
|
13751
14811
|
this.hideLabel();
|
|
13752
14812
|
const center = this.calculatePolygonCenter(polygonData.points);
|
|
13753
|
-
|
|
14813
|
+
let labelHeight;
|
|
14814
|
+
if (this.isClampToGround) {
|
|
14815
|
+
const cachedHeight = this.terrainHeightCache.get(polygonData.id);
|
|
14816
|
+
if (cachedHeight !== void 0) {
|
|
14817
|
+
labelHeight = cachedHeight + 2;
|
|
14818
|
+
} else {
|
|
14819
|
+
const terrainHeight = queryTerrainHeightByLonLatSync(
|
|
14820
|
+
this.CesiumNS,
|
|
14821
|
+
this.viewer,
|
|
14822
|
+
center.lon,
|
|
14823
|
+
center.lat
|
|
14824
|
+
);
|
|
14825
|
+
labelHeight = terrainHeight + 2;
|
|
14826
|
+
}
|
|
14827
|
+
} else {
|
|
14828
|
+
labelHeight = this.polygonHeight;
|
|
14829
|
+
}
|
|
14830
|
+
const position = C.Cartesian3.fromDegrees(center.lon, center.lat, labelHeight);
|
|
13754
14831
|
this.hoverLabel = this.labelCollection.add({
|
|
13755
14832
|
position,
|
|
13756
14833
|
text: polygonData.name,
|
|
@@ -13811,36 +14888,170 @@ var MassPolygonManager = class {
|
|
|
13811
14888
|
}
|
|
13812
14889
|
/**
|
|
13813
14890
|
* 飞行到所有多边形的范围
|
|
14891
|
+
*
|
|
14892
|
+
* 优化说明:
|
|
14893
|
+
* 1. 多点采样地形高度(四角 + 中心),取最大值确保相机不会陷入地下
|
|
14894
|
+
* 2. 修正 pitch 角度对视角高度的影响计算
|
|
14895
|
+
* 3. 添加最小/最大高度限制,防止极端情况
|
|
14896
|
+
* 4. 使用异步地形查询获取更精确的高度
|
|
14897
|
+
* 5. 添加销毁检查,防止异步回调在实例销毁后执行
|
|
13814
14898
|
*/
|
|
13815
14899
|
flyToBounds(options) {
|
|
14900
|
+
if (this.isDestroyed) {
|
|
14901
|
+
console.warn("[MassPolygonManager] \u5B9E\u4F8B\u5DF2\u9500\u6BC1\uFF0C\u8DF3\u8FC7 flyToBounds");
|
|
14902
|
+
return;
|
|
14903
|
+
}
|
|
13816
14904
|
const bounds = this.getBounds();
|
|
13817
14905
|
if (!bounds) {
|
|
13818
14906
|
console.warn("[MassPolygonManager] \u6CA1\u6709\u591A\u8FB9\u5F62\u6570\u636E\uFF0C\u65E0\u6CD5\u98DE\u884C");
|
|
13819
14907
|
return;
|
|
13820
14908
|
}
|
|
14909
|
+
if (bounds.width <= 0 || bounds.height <= 0) {
|
|
14910
|
+
console.warn("[MassPolygonManager] \u591A\u8FB9\u5F62\u8303\u56F4\u65E0\u6548\uFF0C\u8DF3\u8FC7\u98DE\u884C");
|
|
14911
|
+
return;
|
|
14912
|
+
}
|
|
14913
|
+
const C = this.CesiumNS;
|
|
14914
|
+
const camera = this.viewer.camera;
|
|
14915
|
+
const duration = options?.duration ?? 2;
|
|
14916
|
+
const padding = options?.padding ?? 0.2;
|
|
14917
|
+
const pitch = options?.pitch ?? C.Math.toRadians(-45);
|
|
14918
|
+
const heading = options?.heading ?? 0;
|
|
14919
|
+
const minHeight = options?.minHeight ?? 100;
|
|
14920
|
+
const maxHeight = options?.maxHeight ?? 1e5;
|
|
14921
|
+
const paddedWest = bounds.west - bounds.width * padding;
|
|
14922
|
+
const paddedEast = bounds.east + bounds.width * padding;
|
|
14923
|
+
const paddedSouth = bounds.south - bounds.height * padding;
|
|
14924
|
+
const paddedNorth = bounds.north + bounds.height * padding;
|
|
14925
|
+
const samplePoints = [
|
|
14926
|
+
[bounds.centerLon, bounds.centerLat],
|
|
14927
|
+
// 中心
|
|
14928
|
+
[paddedWest, paddedSouth],
|
|
14929
|
+
// 左下
|
|
14930
|
+
[paddedEast, paddedSouth],
|
|
14931
|
+
// 右下
|
|
14932
|
+
[paddedWest, paddedNorth],
|
|
14933
|
+
// 左上
|
|
14934
|
+
[paddedEast, paddedNorth],
|
|
14935
|
+
// 右上
|
|
14936
|
+
[(paddedWest + paddedEast) / 2, paddedSouth],
|
|
14937
|
+
// 下边中点
|
|
14938
|
+
[(paddedWest + paddedEast) / 2, paddedNorth],
|
|
14939
|
+
// 上边中点
|
|
14940
|
+
[paddedWest, (paddedSouth + paddedNorth) / 2],
|
|
14941
|
+
// 左边中点
|
|
14942
|
+
[paddedEast, (paddedSouth + paddedNorth) / 2]
|
|
14943
|
+
// 右边中点
|
|
14944
|
+
];
|
|
14945
|
+
Promise.all(
|
|
14946
|
+
samplePoints.map(
|
|
14947
|
+
([lon, lat]) => queryTerrainHeightByLonLat(this.CesiumNS, this.viewer, lon, lat)
|
|
14948
|
+
)
|
|
14949
|
+
).then((terrainHeights) => {
|
|
14950
|
+
if (this.isDestroyed) {
|
|
14951
|
+
console.warn("[MassPolygonManager] \u5B9E\u4F8B\u5DF2\u9500\u6BC1\uFF0C\u8DF3\u8FC7\u98DE\u884C\u64CD\u4F5C");
|
|
14952
|
+
return;
|
|
14953
|
+
}
|
|
14954
|
+
const is2DMode = this.viewer.scene.mode === C.SceneMode.SCENE2D;
|
|
14955
|
+
const validTerrainHeights = terrainHeights.filter((h) => h >= -500 && h < 1e4);
|
|
14956
|
+
const maxTerrainHeight = is2DMode || validTerrainHeights.length === 0 ? 0 : Math.max(...validTerrainHeights, 0);
|
|
14957
|
+
const earthRadius = 6371e3;
|
|
14958
|
+
const avgLat = (paddedSouth + paddedNorth) / 2;
|
|
14959
|
+
const latRad = avgLat * (Math.PI / 180);
|
|
14960
|
+
const rectWidth = (paddedEast - paddedWest) * (Math.PI / 180) * earthRadius * Math.cos(latRad);
|
|
14961
|
+
const rectHeight = (paddedNorth - paddedSouth) * (Math.PI / 180) * earthRadius;
|
|
14962
|
+
if (!isFinite(rectWidth) || !isFinite(rectHeight) || rectWidth <= 0 || rectHeight <= 0) {
|
|
14963
|
+
console.warn("[MassPolygonManager] \u8303\u56F4\u8BA1\u7B97\u7ED3\u679C\u65E0\u6548\uFF0C\u8DF3\u8FC7\u98DE\u884C");
|
|
14964
|
+
return;
|
|
14965
|
+
}
|
|
14966
|
+
const frustum = camera.frustum;
|
|
14967
|
+
const fovy = frustum.fovy || frustum.fov || C.Math.toRadians(60);
|
|
14968
|
+
const canvas = this.viewer.scene.canvas;
|
|
14969
|
+
const aspectRatio = canvas.width / canvas.height;
|
|
14970
|
+
const absPitch = Math.abs(pitch);
|
|
14971
|
+
const sinPitch = Math.sin(absPitch);
|
|
14972
|
+
let viewHeight;
|
|
14973
|
+
if (absPitch > C.Math.toRadians(80)) {
|
|
14974
|
+
const tanHalfFovy = Math.tan(fovy / 2);
|
|
14975
|
+
const heightForVertical = rectHeight / (2 * tanHalfFovy);
|
|
14976
|
+
const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
|
|
14977
|
+
viewHeight = Math.max(heightForVertical, heightForHorizontal);
|
|
14978
|
+
} else {
|
|
14979
|
+
const tanHalfFovy = Math.tan(fovy / 2);
|
|
14980
|
+
const heightForVertical = sinPitch > 0.1 ? rectHeight / (2 * tanHalfFovy * sinPitch) : rectHeight / (2 * tanHalfFovy);
|
|
14981
|
+
const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
|
|
14982
|
+
viewHeight = Math.max(heightForVertical, heightForHorizontal);
|
|
14983
|
+
}
|
|
14984
|
+
viewHeight *= 1.2;
|
|
14985
|
+
viewHeight = Math.max(minHeight, Math.min(maxHeight, viewHeight));
|
|
14986
|
+
const altitude = maxTerrainHeight + viewHeight;
|
|
14987
|
+
const destination = C.Cartesian3.fromDegrees(bounds.centerLon, bounds.centerLat, altitude);
|
|
14988
|
+
const orientation = { heading, pitch, roll: 0 };
|
|
14989
|
+
if (duration > 0) {
|
|
14990
|
+
camera.flyTo({ destination, orientation, duration });
|
|
14991
|
+
} else {
|
|
14992
|
+
camera.setView({ destination, orientation });
|
|
14993
|
+
}
|
|
14994
|
+
console.log(
|
|
14995
|
+
`[MassPolygonManager] \u98DE\u884C\u5230\u8FB9\u754C: \u4E2D\u5FC3(${bounds.centerLon.toFixed(6)}, ${bounds.centerLat.toFixed(6)}), \u8303\u56F4(${rectWidth.toFixed(0)}m x ${rectHeight.toFixed(0)}m), \u6700\u9AD8\u5730\u5F62: ${maxTerrainHeight.toFixed(0)}m, \u89C6\u89D2\u9AD8\u5EA6: ${viewHeight.toFixed(0)}m, \u76F8\u673A\u9AD8\u5EA6: ${altitude.toFixed(0)}m`
|
|
14996
|
+
);
|
|
14997
|
+
}).catch((error) => {
|
|
14998
|
+
if (this.isDestroyed) {
|
|
14999
|
+
return;
|
|
15000
|
+
}
|
|
15001
|
+
console.warn("[MassPolygonManager] \u5730\u5F62\u67E5\u8BE2\u5931\u8D25\uFF0C\u4F7F\u7528\u540C\u6B65\u65B9\u6CD5:", error);
|
|
15002
|
+
this.flyToBoundsSync(options);
|
|
15003
|
+
});
|
|
15004
|
+
}
|
|
15005
|
+
/**
|
|
15006
|
+
* 同步版本的飞行方法(降级方案)
|
|
15007
|
+
*/
|
|
15008
|
+
flyToBoundsSync(options) {
|
|
15009
|
+
const bounds = this.getBounds();
|
|
15010
|
+
if (!bounds) return;
|
|
13821
15011
|
const C = this.CesiumNS;
|
|
13822
15012
|
const camera = this.viewer.camera;
|
|
13823
15013
|
const duration = options?.duration ?? 2;
|
|
13824
15014
|
const padding = options?.padding ?? 0.2;
|
|
13825
15015
|
const pitch = options?.pitch ?? C.Math.toRadians(-45);
|
|
13826
15016
|
const heading = options?.heading ?? 0;
|
|
15017
|
+
const minHeight = options?.minHeight ?? 100;
|
|
15018
|
+
const maxHeight = options?.maxHeight ?? 1e5;
|
|
13827
15019
|
const paddedWest = bounds.west - bounds.width * padding;
|
|
13828
15020
|
const paddedEast = bounds.east + bounds.width * padding;
|
|
13829
15021
|
const paddedSouth = bounds.south - bounds.height * padding;
|
|
13830
15022
|
const paddedNorth = bounds.north + bounds.height * padding;
|
|
15023
|
+
const terrainHeight = queryTerrainHeightByLonLatSync(
|
|
15024
|
+
this.CesiumNS,
|
|
15025
|
+
this.viewer,
|
|
15026
|
+
bounds.centerLon,
|
|
15027
|
+
bounds.centerLat
|
|
15028
|
+
);
|
|
15029
|
+
const is2DMode = this.viewer.scene.mode === C.SceneMode.SCENE2D;
|
|
15030
|
+
const validTerrainHeight = is2DMode || terrainHeight < -500 || terrainHeight >= 1e4 ? 0 : terrainHeight;
|
|
13831
15031
|
const earthRadius = 6371e3;
|
|
13832
|
-
const
|
|
15032
|
+
const avgLat = (paddedSouth + paddedNorth) / 2;
|
|
15033
|
+
const latRad = avgLat * (Math.PI / 180);
|
|
15034
|
+
const rectWidth = (paddedEast - paddedWest) * (Math.PI / 180) * earthRadius * Math.cos(latRad);
|
|
13833
15035
|
const rectHeight = (paddedNorth - paddedSouth) * (Math.PI / 180) * earthRadius;
|
|
13834
15036
|
const frustum = camera.frustum;
|
|
13835
15037
|
const fovy = frustum.fovy || frustum.fov || C.Math.toRadians(60);
|
|
13836
15038
|
const canvas = this.viewer.scene.canvas;
|
|
13837
15039
|
const aspectRatio = canvas.width / canvas.height;
|
|
13838
|
-
const fovx = 2 * Math.atan(Math.tan(fovy / 2) * aspectRatio);
|
|
13839
15040
|
const absPitch = Math.abs(pitch);
|
|
13840
|
-
const sinPitch = Math.sin(absPitch)
|
|
13841
|
-
const
|
|
13842
|
-
|
|
13843
|
-
|
|
15041
|
+
const sinPitch = Math.sin(absPitch);
|
|
15042
|
+
const tanHalfFovy = Math.tan(fovy / 2);
|
|
15043
|
+
let viewHeight;
|
|
15044
|
+
if (absPitch > C.Math.toRadians(80)) {
|
|
15045
|
+
const heightForVertical = rectHeight / (2 * tanHalfFovy);
|
|
15046
|
+
const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
|
|
15047
|
+
viewHeight = Math.max(heightForVertical, heightForHorizontal);
|
|
15048
|
+
} else {
|
|
15049
|
+
const heightForVertical = sinPitch > 0.1 ? rectHeight / (2 * tanHalfFovy * sinPitch) : rectHeight / (2 * tanHalfFovy);
|
|
15050
|
+
const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
|
|
15051
|
+
viewHeight = Math.max(heightForVertical, heightForHorizontal);
|
|
15052
|
+
}
|
|
15053
|
+
viewHeight = Math.max(minHeight, Math.min(maxHeight, viewHeight * 1.2));
|
|
15054
|
+
const altitude = validTerrainHeight + viewHeight;
|
|
13844
15055
|
const destination = C.Cartesian3.fromDegrees(bounds.centerLon, bounds.centerLat, altitude);
|
|
13845
15056
|
const orientation = { heading, pitch, roll: 0 };
|
|
13846
15057
|
if (duration > 0) {
|
|
@@ -13848,7 +15059,6 @@ var MassPolygonManager = class {
|
|
|
13848
15059
|
} else {
|
|
13849
15060
|
camera.setView({ destination, orientation });
|
|
13850
15061
|
}
|
|
13851
|
-
console.log(`[MassPolygonManager] \u98DE\u884C\u5230\u8FB9\u754C: \u4E2D\u5FC3(${bounds.centerLon.toFixed(6)}, ${bounds.centerLat.toFixed(6)}), \u9AD8\u5EA6: ${altitude.toFixed(0)}m`);
|
|
13852
15062
|
}
|
|
13853
15063
|
/**
|
|
13854
15064
|
* 设置可见性
|
|
@@ -13857,7 +15067,184 @@ var MassPolygonManager = class {
|
|
|
13857
15067
|
if (this.layerCollection) {
|
|
13858
15068
|
this.layerCollection.show = visible;
|
|
13859
15069
|
}
|
|
15070
|
+
if (this.viewer?.scene) {
|
|
15071
|
+
try {
|
|
15072
|
+
this.viewer.scene.requestRender();
|
|
15073
|
+
} catch (e) {
|
|
15074
|
+
}
|
|
15075
|
+
}
|
|
15076
|
+
}
|
|
15077
|
+
/**
|
|
15078
|
+
* 获取图层可见性
|
|
15079
|
+
*/
|
|
15080
|
+
getVisibility() {
|
|
15081
|
+
return this.layerCollection?.show ?? false;
|
|
15082
|
+
}
|
|
15083
|
+
/**
|
|
15084
|
+
* 隐藏整个图层
|
|
15085
|
+
*/
|
|
15086
|
+
hide() {
|
|
15087
|
+
this.setVisibility(false);
|
|
15088
|
+
}
|
|
15089
|
+
/**
|
|
15090
|
+
* 显示整个图层
|
|
15091
|
+
*/
|
|
15092
|
+
show() {
|
|
15093
|
+
this.setVisibility(true);
|
|
15094
|
+
}
|
|
15095
|
+
/**
|
|
15096
|
+
* 切换图层可见性
|
|
15097
|
+
* @returns 切换后的可见性状态
|
|
15098
|
+
*/
|
|
15099
|
+
toggleVisibility() {
|
|
15100
|
+
const newVisibility = !this.getVisibility();
|
|
15101
|
+
this.setVisibility(newVisibility);
|
|
15102
|
+
return newVisibility;
|
|
15103
|
+
}
|
|
15104
|
+
// ==================== 静态标签管理 ====================
|
|
15105
|
+
/**
|
|
15106
|
+
* 显示所有多边形的静态标签(异步版本)
|
|
15107
|
+
* 在每个多边形的中心点显示其名称
|
|
15108
|
+
*
|
|
15109
|
+
* 注意:
|
|
15110
|
+
* 1. LabelCollection 不支持 heightReference 属性
|
|
15111
|
+
* 2. 贴地模式下,使用异步方法查询地形高度来正确放置标签
|
|
15112
|
+
* 3. 通过 disableDepthTestDistance 确保标签始终可见
|
|
15113
|
+
* 4. 查询的地形高度会被缓存,供 hover 标签使用
|
|
15114
|
+
*/
|
|
15115
|
+
async showStaticLabels() {
|
|
15116
|
+
if (!this.staticLabelCollection) return;
|
|
15117
|
+
const C = this.CesiumNS;
|
|
15118
|
+
this.staticLabelCollection.removeAll();
|
|
15119
|
+
const polygonsWithCenters = [];
|
|
15120
|
+
for (const polygon of this.polygonDataMap.values()) {
|
|
15121
|
+
if (!polygon.name) continue;
|
|
15122
|
+
const center = this.calculatePolygonCentroid(polygon.points);
|
|
15123
|
+
polygonsWithCenters.push({ polygon, center });
|
|
15124
|
+
}
|
|
15125
|
+
if (polygonsWithCenters.length === 0) {
|
|
15126
|
+
this.staticLabelsVisible = true;
|
|
15127
|
+
return;
|
|
15128
|
+
}
|
|
15129
|
+
let labelHeights;
|
|
15130
|
+
if (this.isClampToGround) {
|
|
15131
|
+
const coordinates = polygonsWithCenters.map(({ center }) => ({
|
|
15132
|
+
lon: center.lon,
|
|
15133
|
+
lat: center.lat
|
|
15134
|
+
}));
|
|
15135
|
+
try {
|
|
15136
|
+
const terrainHeights = await queryTerrainHeightsByLonLat(
|
|
15137
|
+
this.CesiumNS,
|
|
15138
|
+
this.viewer,
|
|
15139
|
+
coordinates
|
|
15140
|
+
);
|
|
15141
|
+
labelHeights = terrainHeights.map((h) => h + 2);
|
|
15142
|
+
for (let i = 0; i < polygonsWithCenters.length; i++) {
|
|
15143
|
+
const polygonId = polygonsWithCenters[i].polygon.id;
|
|
15144
|
+
this.terrainHeightCache.set(polygonId, terrainHeights[i]);
|
|
15145
|
+
}
|
|
15146
|
+
console.log(`[MassPolygonManager] \u5F02\u6B65\u67E5\u8BE2\u5730\u5F62\u9AD8\u5EA6\u5B8C\u6210\uFF0C\u8303\u56F4: ${Math.min(...terrainHeights).toFixed(1)}m ~ ${Math.max(...terrainHeights).toFixed(1)}m`);
|
|
15147
|
+
} catch (error) {
|
|
15148
|
+
console.warn("[MassPolygonManager] \u5F02\u6B65\u67E5\u8BE2\u5730\u5F62\u9AD8\u5EA6\u5931\u8D25\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u9AD8\u5EA6:", error);
|
|
15149
|
+
labelHeights = polygonsWithCenters.map(() => 2);
|
|
15150
|
+
}
|
|
15151
|
+
} else {
|
|
15152
|
+
labelHeights = polygonsWithCenters.map(() => this.polygonHeight);
|
|
15153
|
+
}
|
|
15154
|
+
if (this.isDestroyed || !this.staticLabelCollection) {
|
|
15155
|
+
console.warn("[MassPolygonManager] \u5B9E\u4F8B\u5DF2\u9500\u6BC1\uFF0C\u8DF3\u8FC7\u521B\u5EFA\u6807\u7B7E");
|
|
15156
|
+
return;
|
|
15157
|
+
}
|
|
15158
|
+
for (let i = 0; i < polygonsWithCenters.length; i++) {
|
|
15159
|
+
const { polygon, center } = polygonsWithCenters[i];
|
|
15160
|
+
const labelHeight = labelHeights[i];
|
|
15161
|
+
const position = C.Cartesian3.fromDegrees(center.lon, center.lat, labelHeight);
|
|
15162
|
+
this.staticLabelCollection.add({
|
|
15163
|
+
position,
|
|
15164
|
+
text: polygon.name,
|
|
15165
|
+
font: this.staticLabelStyle.font,
|
|
15166
|
+
fillColor: C.Color.fromCssColorString(this.staticLabelStyle.fillColor),
|
|
15167
|
+
outlineColor: C.Color.fromCssColorString(this.staticLabelStyle.outlineColor),
|
|
15168
|
+
outlineWidth: this.staticLabelStyle.outlineWidth,
|
|
15169
|
+
style: C.LabelStyle.FILL_AND_OUTLINE,
|
|
15170
|
+
showBackground: this.staticLabelStyle.showBackground,
|
|
15171
|
+
backgroundColor: C.Color.fromCssColorString(this.staticLabelStyle.backgroundColor),
|
|
15172
|
+
backgroundPadding: new C.Cartesian2(this.staticLabelStyle.backgroundPadding[0], this.staticLabelStyle.backgroundPadding[1]),
|
|
15173
|
+
pixelOffset: new C.Cartesian2(this.staticLabelStyle.pixelOffset[0], this.staticLabelStyle.pixelOffset[1]),
|
|
15174
|
+
scale: this.staticLabelStyle.scale,
|
|
15175
|
+
verticalOrigin: C.VerticalOrigin.CENTER,
|
|
15176
|
+
horizontalOrigin: C.HorizontalOrigin.CENTER,
|
|
15177
|
+
// 禁用深度测试,确保标签始终可见(不会被地形遮挡)
|
|
15178
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
|
15179
|
+
});
|
|
15180
|
+
}
|
|
15181
|
+
this.staticLabelsVisible = true;
|
|
15182
|
+
this.viewer.scene.requestRender();
|
|
15183
|
+
console.log(`[MassPolygonManager] \u5DF2\u663E\u793A ${this.staticLabelCollection.length} \u4E2A\u9759\u6001\u6807\u7B7E`);
|
|
15184
|
+
}
|
|
15185
|
+
/**
|
|
15186
|
+
* 预加载所有多边形质心的地形高度(用于 hover 标签)
|
|
15187
|
+
* 在贴地模式下,建议在 create() 后调用此方法
|
|
15188
|
+
*/
|
|
15189
|
+
async preloadTerrainHeights() {
|
|
15190
|
+
if (!this.isClampToGround) return;
|
|
15191
|
+
const polygons = Array.from(this.polygonDataMap.values());
|
|
15192
|
+
if (polygons.length === 0) return;
|
|
15193
|
+
const coordinates = polygons.map((polygon) => {
|
|
15194
|
+
const center = this.calculatePolygonCentroid(polygon.points);
|
|
15195
|
+
return { lon: center.lon, lat: center.lat };
|
|
15196
|
+
});
|
|
15197
|
+
try {
|
|
15198
|
+
const terrainHeights = await queryTerrainHeightsByLonLat(
|
|
15199
|
+
this.CesiumNS,
|
|
15200
|
+
this.viewer,
|
|
15201
|
+
coordinates
|
|
15202
|
+
);
|
|
15203
|
+
for (let i = 0; i < polygons.length; i++) {
|
|
15204
|
+
this.terrainHeightCache.set(polygons[i].id, terrainHeights[i]);
|
|
15205
|
+
}
|
|
15206
|
+
console.log(`[MassPolygonManager] \u9884\u52A0\u8F7D\u5730\u5F62\u9AD8\u5EA6\u5B8C\u6210: ${polygons.length} \u4E2A\u591A\u8FB9\u5F62`);
|
|
15207
|
+
} catch (error) {
|
|
15208
|
+
console.warn("[MassPolygonManager] \u9884\u52A0\u8F7D\u5730\u5F62\u9AD8\u5EA6\u5931\u8D25:", error);
|
|
15209
|
+
}
|
|
15210
|
+
}
|
|
15211
|
+
/**
|
|
15212
|
+
* 隐藏所有多边形的静态标签
|
|
15213
|
+
*/
|
|
15214
|
+
hideStaticLabels() {
|
|
15215
|
+
if (!this.staticLabelCollection) return;
|
|
15216
|
+
this.staticLabelCollection.removeAll();
|
|
15217
|
+
this.staticLabelsVisible = false;
|
|
13860
15218
|
this.viewer.scene.requestRender();
|
|
15219
|
+
console.log("[MassPolygonManager] \u5DF2\u9690\u85CF\u9759\u6001\u6807\u7B7E");
|
|
15220
|
+
}
|
|
15221
|
+
/**
|
|
15222
|
+
* 切换静态标签显示状态
|
|
15223
|
+
* @returns 切换后的显示状态
|
|
15224
|
+
*/
|
|
15225
|
+
async toggleStaticLabels() {
|
|
15226
|
+
if (this.staticLabelsVisible) {
|
|
15227
|
+
this.hideStaticLabels();
|
|
15228
|
+
} else {
|
|
15229
|
+
await this.showStaticLabels();
|
|
15230
|
+
}
|
|
15231
|
+
return this.staticLabelsVisible;
|
|
15232
|
+
}
|
|
15233
|
+
/**
|
|
15234
|
+
* 获取静态标签显示状态
|
|
15235
|
+
*/
|
|
15236
|
+
getStaticLabelsVisible() {
|
|
15237
|
+
return this.staticLabelsVisible;
|
|
15238
|
+
}
|
|
15239
|
+
/**
|
|
15240
|
+
* 设置静态标签样式
|
|
15241
|
+
* @param style 样式配置(部分)
|
|
15242
|
+
*/
|
|
15243
|
+
async setStaticLabelStyle(style) {
|
|
15244
|
+
this.staticLabelStyle = { ...this.staticLabelStyle, ...style };
|
|
15245
|
+
if (this.staticLabelsVisible) {
|
|
15246
|
+
await this.showStaticLabels();
|
|
15247
|
+
}
|
|
13861
15248
|
}
|
|
13862
15249
|
/**
|
|
13863
15250
|
* 更新样式(需要重新创建所有多边形)
|
|
@@ -13881,6 +15268,10 @@ var MassPolygonManager = class {
|
|
|
13881
15268
|
this.clickHandler.destroy();
|
|
13882
15269
|
this.clickHandler = void 0;
|
|
13883
15270
|
}
|
|
15271
|
+
if (this.rightClickHandler) {
|
|
15272
|
+
this.rightClickHandler.destroy();
|
|
15273
|
+
this.rightClickHandler = void 0;
|
|
15274
|
+
}
|
|
13884
15275
|
this.highlightedId = void 0;
|
|
13885
15276
|
this.selectedId = void 0;
|
|
13886
15277
|
this.originalHighlightFillColor = void 0;
|
|
@@ -13888,20 +15279,35 @@ var MassPolygonManager = class {
|
|
|
13888
15279
|
this.originalSelectFillColor = void 0;
|
|
13889
15280
|
this.originalSelectOutlineColor = void 0;
|
|
13890
15281
|
this.hoverLabel = void 0;
|
|
13891
|
-
|
|
13892
|
-
|
|
15282
|
+
this.highlightPrimitive = void 0;
|
|
15283
|
+
this.selectPrimitive = void 0;
|
|
15284
|
+
if (this.layerCollection && this.viewer?.scene?.primitives) {
|
|
15285
|
+
try {
|
|
15286
|
+
this.viewer.scene.primitives.remove(this.layerCollection);
|
|
15287
|
+
} catch (e) {
|
|
15288
|
+
}
|
|
13893
15289
|
this.layerCollection = void 0;
|
|
13894
15290
|
}
|
|
13895
15291
|
this.primitive = void 0;
|
|
13896
15292
|
this.outlinePrimitive = void 0;
|
|
15293
|
+
this.groundOutlinePrimitive = void 0;
|
|
13897
15294
|
this.labelCollection = void 0;
|
|
15295
|
+
this.staticLabelCollection = void 0;
|
|
15296
|
+
this.staticLabelsVisible = false;
|
|
13898
15297
|
this.polygonDataMap.clear();
|
|
13899
|
-
this.
|
|
15298
|
+
this.terrainHeightCache.clear();
|
|
15299
|
+
if (this.viewer?.scene) {
|
|
15300
|
+
try {
|
|
15301
|
+
this.viewer.scene.requestRender();
|
|
15302
|
+
} catch (e) {
|
|
15303
|
+
}
|
|
15304
|
+
}
|
|
13900
15305
|
}
|
|
13901
15306
|
/**
|
|
13902
15307
|
* 销毁并释放资源
|
|
13903
15308
|
*/
|
|
13904
15309
|
destroy() {
|
|
15310
|
+
this.isDestroyed = true;
|
|
13905
15311
|
this.clear();
|
|
13906
15312
|
}
|
|
13907
15313
|
/**
|
|
@@ -14295,6 +15701,7 @@ exports.CameraManager = CameraManager;
|
|
|
14295
15701
|
exports.Emitter = Emitter;
|
|
14296
15702
|
exports.FlightSimulator = FlightSimulator;
|
|
14297
15703
|
exports.FrustumPyramid = FrustumPyramid;
|
|
15704
|
+
exports.ImageryManager = ImageryManager;
|
|
14298
15705
|
exports.LODManager = LODManager;
|
|
14299
15706
|
exports.LayerManager = LayerManager;
|
|
14300
15707
|
exports.MassPolygonManager = MassPolygonManager;
|
|
@@ -14305,6 +15712,8 @@ exports.RealtimeFlightTracker = RealtimeFlightTracker;
|
|
|
14305
15712
|
exports.SceneManager = SceneManager;
|
|
14306
15713
|
exports.Selector = Selector;
|
|
14307
15714
|
exports.StateManager = StateManager;
|
|
15715
|
+
exports.TerrainManager = TerrainManager;
|
|
15716
|
+
exports.TilesetManager = TilesetManager;
|
|
14308
15717
|
exports.assertCesiumAssetsConfigured = assertCesiumAssetsConfigured;
|
|
14309
15718
|
exports.bringDataSourceToTop = bringDataSourceToTop;
|
|
14310
15719
|
exports.bringPrimitiveCollectionToTop = bringPrimitiveCollectionToTop;
|