@jorgmoritz/gis-manager 0.1.49 → 0.1.50

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 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.48"};
16
+ version: "0.1.50"};
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. Optionally clear existing base imagery layers.
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
- // private tilese3DtMap: Map<string, any> = new Map();
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
- const layer = this.layerManager.getImageryLayer(handle.id);
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,18 @@ 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, "isDestroyed", false);
13353
14133
  // ========== 编辑支持方法 ==========
13354
14134
  // 隐藏的多边形原始颜色存储
13355
14135
  __publicField(this, "hiddenPolygonColors", /* @__PURE__ */ new Map());
@@ -13363,6 +14143,7 @@ var MassPolygonManager = class {
13363
14143
  this.interactionOptions = {
13364
14144
  enableHover: false,
13365
14145
  enableClick: false,
14146
+ enableRightClick: false,
13366
14147
  highlightStyle: {
13367
14148
  fillColor: "rgba(255, 255, 0, 0.7)",
13368
14149
  outlineColor: "rgba(255, 255, 0, 1)"
@@ -13382,7 +14163,8 @@ var MassPolygonManager = class {
13382
14163
  pixelOffset: [0, -20]
13383
14164
  },
13384
14165
  onHover: void 0,
13385
- onClick: void 0
14166
+ onClick: void 0,
14167
+ onRightClick: void 0
13386
14168
  };
13387
14169
  }
13388
14170
  /**
@@ -13414,6 +14196,7 @@ var MassPolygonManager = class {
13414
14196
  this.interactionOptions = {
13415
14197
  enableHover: interaction.enableHover ?? false,
13416
14198
  enableClick: interaction.enableClick ?? false,
14199
+ enableRightClick: interaction.enableRightClick ?? false,
13417
14200
  highlightStyle: {
13418
14201
  fillColor: interaction.highlightStyle?.fillColor ?? this.interactionOptions.highlightStyle.fillColor,
13419
14202
  outlineColor: interaction.highlightStyle?.outlineColor ?? this.interactionOptions.highlightStyle.outlineColor
@@ -13433,7 +14216,8 @@ var MassPolygonManager = class {
13433
14216
  pixelOffset: interaction.hoverLabelStyle?.pixelOffset ?? this.interactionOptions.hoverLabelStyle.pixelOffset
13434
14217
  },
13435
14218
  onHover: interaction.onHover,
13436
- onClick: interaction.onClick
14219
+ onClick: interaction.onClick,
14220
+ onRightClick: interaction.onRightClick
13437
14221
  };
13438
14222
  }
13439
14223
  this.isClampToGround = options?.clampToGround ?? false;
@@ -13458,46 +14242,98 @@ var MassPolygonManager = class {
13458
14242
  continue;
13459
14243
  }
13460
14244
  const positions = this.convertPointsToCartesian(polygon.points);
13461
- const polygonGeometry = new C.PolygonGeometry({
13462
- polygonHierarchy: new C.PolygonHierarchy(positions),
13463
- perPositionHeight: false,
13464
- height: this.polygonHeight
13465
- // 使用配置的高度
13466
- });
13467
- fillInstances.push(new C.GeometryInstance({
13468
- geometry: polygonGeometry,
13469
- id: polygon.id,
13470
- attributes: {
13471
- color: C.ColorGeometryInstanceAttribute.fromColor(fillColor),
13472
- show: new C.ShowGeometryInstanceAttribute(true)
13473
- }
13474
- }));
13475
- const outlinePositionsWithHeight = polygon.points.map(
13476
- (p) => C.Cartesian3.fromDegrees(p.lon, p.lat, this.polygonHeight)
14245
+ if (this.isClampToGround) {
14246
+ const polygonGeometry = new C.PolygonGeometry({
14247
+ polygonHierarchy: new C.PolygonHierarchy(positions)
14248
+ });
14249
+ fillInstances.push(new C.GeometryInstance({
14250
+ geometry: polygonGeometry,
14251
+ id: polygon.id,
14252
+ attributes: {
14253
+ color: C.ColorGeometryInstanceAttribute.fromColor(fillColor)
14254
+ }
14255
+ }));
14256
+ } else {
14257
+ const polygonGeometry = new C.PolygonGeometry({
14258
+ polygonHierarchy: new C.PolygonHierarchy(positions),
14259
+ perPositionHeight: false,
14260
+ height: this.polygonHeight
14261
+ });
14262
+ fillInstances.push(new C.GeometryInstance({
14263
+ geometry: polygonGeometry,
14264
+ id: polygon.id,
14265
+ attributes: {
14266
+ color: C.ColorGeometryInstanceAttribute.fromColor(fillColor),
14267
+ show: new C.ShowGeometryInstanceAttribute(true)
14268
+ }
14269
+ }));
14270
+ }
14271
+ const outlinePositions = polygon.points.map(
14272
+ (p) => C.Cartesian3.fromDegrees(p.lon, p.lat, this.isClampToGround ? 0 : this.polygonHeight)
13477
14273
  );
13478
- outlinePositionsWithHeight.push(outlinePositionsWithHeight[0]);
13479
- const outlineGeometry = new C.PolylineGeometry({
13480
- positions: outlinePositionsWithHeight,
13481
- width: this.currentStyle.outlineWidth
13482
- });
13483
- outlineInstances.push(new C.GeometryInstance({
13484
- geometry: outlineGeometry,
13485
- id: `${polygon.id}-outline`,
13486
- attributes: {
13487
- color: C.ColorGeometryInstanceAttribute.fromColor(outlineColor),
13488
- show: new C.ShowGeometryInstanceAttribute(true)
13489
- }
13490
- }));
14274
+ outlinePositions.push(outlinePositions[0]);
14275
+ if (this.isClampToGround) {
14276
+ const groundOutlineGeometry = new C.GroundPolylineGeometry({
14277
+ positions: outlinePositions,
14278
+ width: this.currentStyle.outlineWidth
14279
+ });
14280
+ outlineInstances.push(new C.GeometryInstance({
14281
+ geometry: groundOutlineGeometry,
14282
+ id: `${polygon.id}-outline`,
14283
+ attributes: {
14284
+ color: C.ColorGeometryInstanceAttribute.fromColor(outlineColor)
14285
+ }
14286
+ }));
14287
+ } else {
14288
+ const outlineGeometry = new C.PolylineGeometry({
14289
+ positions: outlinePositions,
14290
+ width: this.currentStyle.outlineWidth
14291
+ });
14292
+ outlineInstances.push(new C.GeometryInstance({
14293
+ geometry: outlineGeometry,
14294
+ id: `${polygon.id}-outline`,
14295
+ attributes: {
14296
+ color: C.ColorGeometryInstanceAttribute.fromColor(outlineColor),
14297
+ show: new C.ShowGeometryInstanceAttribute(true)
14298
+ }
14299
+ }));
14300
+ }
13491
14301
  }
13492
14302
  if (fillInstances.length > 0) {
13493
14303
  const appearance = new C.PerInstanceColorAppearance({ flat: true, translucent: true });
13494
- this.primitive = new C.Primitive({ geometryInstances: fillInstances, appearance, asynchronous });
14304
+ if (this.isClampToGround) {
14305
+ this.primitive = new C.GroundPrimitive({
14306
+ geometryInstances: fillInstances,
14307
+ appearance,
14308
+ asynchronous,
14309
+ releaseGeometryInstances: false,
14310
+ allowPicking: true
14311
+ });
14312
+ } else {
14313
+ this.primitive = new C.Primitive({
14314
+ geometryInstances: fillInstances,
14315
+ appearance,
14316
+ asynchronous,
14317
+ releaseGeometryInstances: false,
14318
+ allowPicking: true
14319
+ });
14320
+ }
13495
14321
  this.layerCollection.add(this.primitive);
13496
14322
  }
13497
14323
  if (outlineInstances.length > 0) {
13498
- const appearance = new C.PolylineColorAppearance();
13499
- this.outlinePrimitive = new C.Primitive({ geometryInstances: outlineInstances, appearance, asynchronous });
13500
- this.layerCollection.add(this.outlinePrimitive);
14324
+ if (this.isClampToGround) {
14325
+ const appearance = new C.PolylineColorAppearance();
14326
+ this.groundOutlinePrimitive = new C.GroundPolylinePrimitive({
14327
+ geometryInstances: outlineInstances,
14328
+ appearance,
14329
+ asynchronous
14330
+ });
14331
+ this.layerCollection.add(this.groundOutlinePrimitive);
14332
+ } else {
14333
+ const appearance = new C.PolylineColorAppearance();
14334
+ this.outlinePrimitive = new C.Primitive({ geometryInstances: outlineInstances, appearance, asynchronous });
14335
+ this.layerCollection.add(this.outlinePrimitive);
14336
+ }
13501
14337
  }
13502
14338
  this.labelCollection = new C.LabelCollection();
13503
14339
  this.layerCollection.add(this.labelCollection);
@@ -13507,6 +14343,9 @@ var MassPolygonManager = class {
13507
14343
  if (this.interactionOptions.enableClick) {
13508
14344
  this.setupClickHandler();
13509
14345
  }
14346
+ if (this.interactionOptions.enableRightClick) {
14347
+ this.setupRightClickHandler();
14348
+ }
13510
14349
  this.viewer.scene.requestRender();
13511
14350
  return {
13512
14351
  primitive: this.primitive,
@@ -13527,19 +14366,25 @@ var MassPolygonManager = class {
13527
14366
  const now = Date.now();
13528
14367
  if (now - this.lastPickTime < this.pickThrottleMs) return;
13529
14368
  this.lastPickTime = now;
13530
- const pickedObject = this.viewer.scene.pick(movement.endPosition);
13531
- if (pickedObject?.id && typeof pickedObject.id === "string") {
13532
- const polygonId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
13533
- if (this.polygonDataMap.has(polygonId)) {
13534
- if (this.highlightedId !== polygonId) {
13535
- this.clearHighlight();
13536
- this.highlightPolygon(polygonId);
13537
- const data = this.polygonDataMap.get(polygonId);
13538
- if (data) this.showLabel(data);
13539
- this.interactionOptions.onHover?.(polygonId, data ?? null);
14369
+ let polygonId = null;
14370
+ if (this.isClampToGround) {
14371
+ polygonId = this.pickPolygonByPosition(movement.endPosition);
14372
+ } else {
14373
+ const pickedObject = this.viewer.scene.pick(movement.endPosition);
14374
+ if (pickedObject?.id && typeof pickedObject.id === "string") {
14375
+ const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
14376
+ if (this.polygonDataMap.has(pickedId)) {
14377
+ polygonId = pickedId;
13540
14378
  }
13541
- } else {
13542
- this.handleMouseLeave();
14379
+ }
14380
+ }
14381
+ if (polygonId) {
14382
+ if (this.highlightedId !== polygonId) {
14383
+ this.clearHighlight();
14384
+ this.highlightPolygon(polygonId);
14385
+ const data = this.polygonDataMap.get(polygonId);
14386
+ if (data) this.showLabel(data);
14387
+ this.interactionOptions.onHover?.(polygonId, data ?? null);
13543
14388
  }
13544
14389
  } else {
13545
14390
  this.handleMouseLeave();
@@ -13559,23 +14404,26 @@ var MassPolygonManager = class {
13559
14404
  this.clickHandler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
13560
14405
  this.clickHandler.setInputAction(
13561
14406
  (movement) => {
13562
- const pickedObject = this.viewer.scene.pick(movement.position);
13563
- if (pickedObject?.id && typeof pickedObject.id === "string") {
13564
- const polygonId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
13565
- if (this.polygonDataMap.has(polygonId)) {
13566
- if (this.selectedId === polygonId) {
13567
- this.deselect();
13568
- this.interactionOptions.onClick?.(null, null);
13569
- } else {
13570
- this.select(polygonId);
13571
- const data = this.polygonDataMap.get(polygonId);
13572
- this.interactionOptions.onClick?.(polygonId, data ?? null);
14407
+ let polygonId = null;
14408
+ if (this.isClampToGround) {
14409
+ polygonId = this.pickPolygonByPosition(movement.position);
14410
+ } else {
14411
+ const pickedObject = this.viewer.scene.pick(movement.position);
14412
+ if (pickedObject?.id && typeof pickedObject.id === "string") {
14413
+ const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
14414
+ if (this.polygonDataMap.has(pickedId)) {
14415
+ polygonId = pickedId;
13573
14416
  }
14417
+ }
14418
+ }
14419
+ if (polygonId) {
14420
+ if (this.selectedId === polygonId) {
14421
+ this.deselect();
14422
+ this.interactionOptions.onClick?.(null, null);
13574
14423
  } else {
13575
- if (this.selectedId) {
13576
- this.deselect();
13577
- this.interactionOptions.onClick?.(null, null);
13578
- }
14424
+ this.select(polygonId);
14425
+ const data = this.polygonDataMap.get(polygonId);
14426
+ this.interactionOptions.onClick?.(polygonId, data ?? null);
13579
14427
  }
13580
14428
  } else {
13581
14429
  if (this.selectedId) {
@@ -13587,6 +14435,79 @@ var MassPolygonManager = class {
13587
14435
  C.ScreenSpaceEventType.LEFT_CLICK
13588
14436
  );
13589
14437
  }
14438
+ /**
14439
+ * 设置右键事件处理器
14440
+ */
14441
+ setupRightClickHandler() {
14442
+ if (this.rightClickHandler) {
14443
+ this.rightClickHandler.destroy();
14444
+ }
14445
+ const C = this.CesiumNS;
14446
+ this.rightClickHandler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
14447
+ this.rightClickHandler.setInputAction(
14448
+ (movement) => {
14449
+ let polygonId = null;
14450
+ if (this.isClampToGround) {
14451
+ polygonId = this.pickPolygonByPosition(movement.position);
14452
+ } else {
14453
+ const pickedObject = this.viewer.scene.pick(movement.position);
14454
+ if (pickedObject?.id && typeof pickedObject.id === "string") {
14455
+ const pickedId = pickedObject.id.endsWith("-outline") ? pickedObject.id.replace("-outline", "") : pickedObject.id;
14456
+ if (this.polygonDataMap.has(pickedId)) {
14457
+ polygonId = pickedId;
14458
+ }
14459
+ }
14460
+ }
14461
+ if (polygonId) {
14462
+ this.select(polygonId);
14463
+ const data = this.polygonDataMap.get(polygonId);
14464
+ if (data) {
14465
+ const canvas = this.viewer.scene.canvas;
14466
+ const canvasRect = canvas.getBoundingClientRect();
14467
+ const screenX = canvasRect.left + movement.position.x;
14468
+ const screenY = canvasRect.top + movement.position.y;
14469
+ this.interactionOptions.onRightClick?.(polygonId, data, { x: screenX, y: screenY });
14470
+ }
14471
+ }
14472
+ },
14473
+ C.ScreenSpaceEventType.RIGHT_CLICK
14474
+ );
14475
+ }
14476
+ /**
14477
+ * 通过屏幕坐标获取地理位置,然后判断在哪个多边形内
14478
+ * 用于贴地模式下的精确拾取
14479
+ */
14480
+ pickPolygonByPosition(screenPosition) {
14481
+ const C = this.CesiumNS;
14482
+ const ray = this.viewer.camera.getPickRay(screenPosition);
14483
+ if (!ray) return null;
14484
+ const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
14485
+ if (!cartesian) return null;
14486
+ const cartographic = C.Cartographic.fromCartesian(cartesian);
14487
+ const lon = C.Math.toDegrees(cartographic.longitude);
14488
+ const lat = C.Math.toDegrees(cartographic.latitude);
14489
+ for (const [id, polygon] of this.polygonDataMap) {
14490
+ if (this.isPointInPolygon(lon, lat, polygon.points)) {
14491
+ return id;
14492
+ }
14493
+ }
14494
+ return null;
14495
+ }
14496
+ /**
14497
+ * 判断点是否在多边形内(射线法)
14498
+ */
14499
+ isPointInPolygon(lon, lat, points) {
14500
+ let inside = false;
14501
+ const n = points.length;
14502
+ for (let i = 0, j = n - 1; i < n; j = i++) {
14503
+ const xi = points[i].lon, yi = points[i].lat;
14504
+ const xj = points[j].lon, yj = points[j].lat;
14505
+ if (yi > lat !== yj > lat && lon < (xj - xi) * (lat - yi) / (yj - yi) + xi) {
14506
+ inside = !inside;
14507
+ }
14508
+ }
14509
+ return inside;
14510
+ }
13590
14511
  /**
13591
14512
  * 处理鼠标离开
13592
14513
  */
@@ -13604,6 +14525,11 @@ var MassPolygonManager = class {
13604
14525
  if (id === this.selectedId) return;
13605
14526
  const C = this.CesiumNS;
13606
14527
  if (!this.primitive) return;
14528
+ if (this.isClampToGround) {
14529
+ this.highlightPolygonWithSeparatePrimitive(id, "highlight");
14530
+ this.highlightedId = id;
14531
+ return;
14532
+ }
13607
14533
  try {
13608
14534
  const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(id);
13609
14535
  if (fillAttributes?.color) {
@@ -13624,11 +14550,60 @@ var MassPolygonManager = class {
13624
14550
  } catch (e) {
13625
14551
  }
13626
14552
  }
14553
+ /**
14554
+ * 贴地模式下使用独立 Primitive 高亮/选中多边形
14555
+ */
14556
+ highlightPolygonWithSeparatePrimitive(id, type) {
14557
+ const C = this.CesiumNS;
14558
+ const polygonData = this.polygonDataMap.get(id);
14559
+ if (!polygonData) return;
14560
+ if (type === "highlight" && this.highlightPrimitive) {
14561
+ this.layerCollection?.remove(this.highlightPrimitive);
14562
+ this.highlightPrimitive = void 0;
14563
+ } else if (type === "select" && this.selectPrimitive) {
14564
+ this.layerCollection?.remove(this.selectPrimitive);
14565
+ this.selectPrimitive = void 0;
14566
+ }
14567
+ const positions = this.convertPointsToCartesian(polygonData.points);
14568
+ const color = type === "highlight" ? C.Color.fromCssColorString(this.interactionOptions.highlightStyle.fillColor) : C.Color.fromCssColorString(this.interactionOptions.selectStyle.fillColor);
14569
+ const geometry = new C.PolygonGeometry({
14570
+ polygonHierarchy: new C.PolygonHierarchy(positions)
14571
+ });
14572
+ const instance = new C.GeometryInstance({
14573
+ geometry,
14574
+ id: `${id}-${type}`,
14575
+ attributes: {
14576
+ color: C.ColorGeometryInstanceAttribute.fromColor(color)
14577
+ }
14578
+ });
14579
+ const primitive = new C.GroundPrimitive({
14580
+ geometryInstances: instance,
14581
+ appearance: new C.PerInstanceColorAppearance({ flat: true, translucent: true }),
14582
+ asynchronous: false
14583
+ // 同步创建,立即显示
14584
+ });
14585
+ if (type === "highlight") {
14586
+ this.highlightPrimitive = primitive;
14587
+ } else {
14588
+ this.selectPrimitive = primitive;
14589
+ }
14590
+ this.layerCollection?.add(primitive);
14591
+ this.viewer.scene.requestRender();
14592
+ }
13627
14593
  /**
13628
14594
  * 清除悬停高亮
13629
14595
  */
13630
14596
  clearHighlight() {
13631
14597
  if (!this.highlightedId) return;
14598
+ if (this.isClampToGround) {
14599
+ if (this.highlightPrimitive) {
14600
+ this.layerCollection?.remove(this.highlightPrimitive);
14601
+ this.highlightPrimitive = void 0;
14602
+ }
14603
+ this.highlightedId = void 0;
14604
+ this.viewer.scene.requestRender();
14605
+ return;
14606
+ }
13632
14607
  try {
13633
14608
  if (this.primitive && this.originalHighlightFillColor) {
13634
14609
  const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(this.highlightedId);
@@ -13664,6 +14639,11 @@ var MassPolygonManager = class {
13664
14639
  }
13665
14640
  const C = this.CesiumNS;
13666
14641
  if (!this.primitive) return false;
14642
+ if (this.isClampToGround) {
14643
+ this.highlightPolygonWithSeparatePrimitive(id, "select");
14644
+ this.selectedId = id;
14645
+ return true;
14646
+ }
13667
14647
  try {
13668
14648
  const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(id);
13669
14649
  if (fillAttributes?.color) {
@@ -13691,6 +14671,15 @@ var MassPolygonManager = class {
13691
14671
  */
13692
14672
  deselect() {
13693
14673
  if (!this.selectedId) return;
14674
+ if (this.isClampToGround) {
14675
+ if (this.selectPrimitive) {
14676
+ this.layerCollection?.remove(this.selectPrimitive);
14677
+ this.selectPrimitive = void 0;
14678
+ }
14679
+ this.selectedId = void 0;
14680
+ this.viewer.scene.requestRender();
14681
+ return;
14682
+ }
13694
14683
  try {
13695
14684
  if (this.primitive && this.originalSelectFillColor) {
13696
14685
  const fillAttributes = this.primitive.getGeometryInstanceAttributes?.(this.selectedId);
@@ -13811,36 +14800,170 @@ var MassPolygonManager = class {
13811
14800
  }
13812
14801
  /**
13813
14802
  * 飞行到所有多边形的范围
14803
+ *
14804
+ * 优化说明:
14805
+ * 1. 多点采样地形高度(四角 + 中心),取最大值确保相机不会陷入地下
14806
+ * 2. 修正 pitch 角度对视角高度的影响计算
14807
+ * 3. 添加最小/最大高度限制,防止极端情况
14808
+ * 4. 使用异步地形查询获取更精确的高度
14809
+ * 5. 添加销毁检查,防止异步回调在实例销毁后执行
13814
14810
  */
13815
14811
  flyToBounds(options) {
14812
+ if (this.isDestroyed) {
14813
+ console.warn("[MassPolygonManager] \u5B9E\u4F8B\u5DF2\u9500\u6BC1\uFF0C\u8DF3\u8FC7 flyToBounds");
14814
+ return;
14815
+ }
13816
14816
  const bounds = this.getBounds();
13817
14817
  if (!bounds) {
13818
14818
  console.warn("[MassPolygonManager] \u6CA1\u6709\u591A\u8FB9\u5F62\u6570\u636E\uFF0C\u65E0\u6CD5\u98DE\u884C");
13819
14819
  return;
13820
14820
  }
14821
+ if (bounds.width <= 0 || bounds.height <= 0) {
14822
+ console.warn("[MassPolygonManager] \u591A\u8FB9\u5F62\u8303\u56F4\u65E0\u6548\uFF0C\u8DF3\u8FC7\u98DE\u884C");
14823
+ return;
14824
+ }
14825
+ const C = this.CesiumNS;
14826
+ const camera = this.viewer.camera;
14827
+ const duration = options?.duration ?? 2;
14828
+ const padding = options?.padding ?? 0.2;
14829
+ const pitch = options?.pitch ?? C.Math.toRadians(-45);
14830
+ const heading = options?.heading ?? 0;
14831
+ const minHeight = options?.minHeight ?? 100;
14832
+ const maxHeight = options?.maxHeight ?? 1e5;
14833
+ const paddedWest = bounds.west - bounds.width * padding;
14834
+ const paddedEast = bounds.east + bounds.width * padding;
14835
+ const paddedSouth = bounds.south - bounds.height * padding;
14836
+ const paddedNorth = bounds.north + bounds.height * padding;
14837
+ const samplePoints = [
14838
+ [bounds.centerLon, bounds.centerLat],
14839
+ // 中心
14840
+ [paddedWest, paddedSouth],
14841
+ // 左下
14842
+ [paddedEast, paddedSouth],
14843
+ // 右下
14844
+ [paddedWest, paddedNorth],
14845
+ // 左上
14846
+ [paddedEast, paddedNorth],
14847
+ // 右上
14848
+ [(paddedWest + paddedEast) / 2, paddedSouth],
14849
+ // 下边中点
14850
+ [(paddedWest + paddedEast) / 2, paddedNorth],
14851
+ // 上边中点
14852
+ [paddedWest, (paddedSouth + paddedNorth) / 2],
14853
+ // 左边中点
14854
+ [paddedEast, (paddedSouth + paddedNorth) / 2]
14855
+ // 右边中点
14856
+ ];
14857
+ Promise.all(
14858
+ samplePoints.map(
14859
+ ([lon, lat]) => queryTerrainHeightByLonLat(this.CesiumNS, this.viewer, lon, lat)
14860
+ )
14861
+ ).then((terrainHeights) => {
14862
+ if (this.isDestroyed) {
14863
+ console.warn("[MassPolygonManager] \u5B9E\u4F8B\u5DF2\u9500\u6BC1\uFF0C\u8DF3\u8FC7\u98DE\u884C\u64CD\u4F5C");
14864
+ return;
14865
+ }
14866
+ const is2DMode = this.viewer.scene.mode === C.SceneMode.SCENE2D;
14867
+ const validTerrainHeights = terrainHeights.filter((h) => h >= -500 && h < 1e4);
14868
+ const maxTerrainHeight = is2DMode || validTerrainHeights.length === 0 ? 0 : Math.max(...validTerrainHeights, 0);
14869
+ const earthRadius = 6371e3;
14870
+ const avgLat = (paddedSouth + paddedNorth) / 2;
14871
+ const latRad = avgLat * (Math.PI / 180);
14872
+ const rectWidth = (paddedEast - paddedWest) * (Math.PI / 180) * earthRadius * Math.cos(latRad);
14873
+ const rectHeight = (paddedNorth - paddedSouth) * (Math.PI / 180) * earthRadius;
14874
+ if (!isFinite(rectWidth) || !isFinite(rectHeight) || rectWidth <= 0 || rectHeight <= 0) {
14875
+ console.warn("[MassPolygonManager] \u8303\u56F4\u8BA1\u7B97\u7ED3\u679C\u65E0\u6548\uFF0C\u8DF3\u8FC7\u98DE\u884C");
14876
+ return;
14877
+ }
14878
+ const frustum = camera.frustum;
14879
+ const fovy = frustum.fovy || frustum.fov || C.Math.toRadians(60);
14880
+ const canvas = this.viewer.scene.canvas;
14881
+ const aspectRatio = canvas.width / canvas.height;
14882
+ const absPitch = Math.abs(pitch);
14883
+ const sinPitch = Math.sin(absPitch);
14884
+ let viewHeight;
14885
+ if (absPitch > C.Math.toRadians(80)) {
14886
+ const tanHalfFovy = Math.tan(fovy / 2);
14887
+ const heightForVertical = rectHeight / (2 * tanHalfFovy);
14888
+ const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
14889
+ viewHeight = Math.max(heightForVertical, heightForHorizontal);
14890
+ } else {
14891
+ const tanHalfFovy = Math.tan(fovy / 2);
14892
+ const heightForVertical = sinPitch > 0.1 ? rectHeight / (2 * tanHalfFovy * sinPitch) : rectHeight / (2 * tanHalfFovy);
14893
+ const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
14894
+ viewHeight = Math.max(heightForVertical, heightForHorizontal);
14895
+ }
14896
+ viewHeight *= 1.2;
14897
+ viewHeight = Math.max(minHeight, Math.min(maxHeight, viewHeight));
14898
+ const altitude = maxTerrainHeight + viewHeight;
14899
+ const destination = C.Cartesian3.fromDegrees(bounds.centerLon, bounds.centerLat, altitude);
14900
+ const orientation = { heading, pitch, roll: 0 };
14901
+ if (duration > 0) {
14902
+ camera.flyTo({ destination, orientation, duration });
14903
+ } else {
14904
+ camera.setView({ destination, orientation });
14905
+ }
14906
+ console.log(
14907
+ `[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`
14908
+ );
14909
+ }).catch((error) => {
14910
+ if (this.isDestroyed) {
14911
+ return;
14912
+ }
14913
+ console.warn("[MassPolygonManager] \u5730\u5F62\u67E5\u8BE2\u5931\u8D25\uFF0C\u4F7F\u7528\u540C\u6B65\u65B9\u6CD5:", error);
14914
+ this.flyToBoundsSync(options);
14915
+ });
14916
+ }
14917
+ /**
14918
+ * 同步版本的飞行方法(降级方案)
14919
+ */
14920
+ flyToBoundsSync(options) {
14921
+ const bounds = this.getBounds();
14922
+ if (!bounds) return;
13821
14923
  const C = this.CesiumNS;
13822
14924
  const camera = this.viewer.camera;
13823
14925
  const duration = options?.duration ?? 2;
13824
14926
  const padding = options?.padding ?? 0.2;
13825
14927
  const pitch = options?.pitch ?? C.Math.toRadians(-45);
13826
14928
  const heading = options?.heading ?? 0;
14929
+ const minHeight = options?.minHeight ?? 100;
14930
+ const maxHeight = options?.maxHeight ?? 1e5;
13827
14931
  const paddedWest = bounds.west - bounds.width * padding;
13828
14932
  const paddedEast = bounds.east + bounds.width * padding;
13829
14933
  const paddedSouth = bounds.south - bounds.height * padding;
13830
14934
  const paddedNorth = bounds.north + bounds.height * padding;
14935
+ const terrainHeight = queryTerrainHeightByLonLatSync(
14936
+ this.CesiumNS,
14937
+ this.viewer,
14938
+ bounds.centerLon,
14939
+ bounds.centerLat
14940
+ );
14941
+ const is2DMode = this.viewer.scene.mode === C.SceneMode.SCENE2D;
14942
+ const validTerrainHeight = is2DMode || terrainHeight < -500 || terrainHeight >= 1e4 ? 0 : terrainHeight;
13831
14943
  const earthRadius = 6371e3;
13832
- const rectWidth = (paddedEast - paddedWest) * (Math.PI / 180) * earthRadius * Math.cos(bounds.centerLat * Math.PI / 180);
14944
+ const avgLat = (paddedSouth + paddedNorth) / 2;
14945
+ const latRad = avgLat * (Math.PI / 180);
14946
+ const rectWidth = (paddedEast - paddedWest) * (Math.PI / 180) * earthRadius * Math.cos(latRad);
13833
14947
  const rectHeight = (paddedNorth - paddedSouth) * (Math.PI / 180) * earthRadius;
13834
14948
  const frustum = camera.frustum;
13835
14949
  const fovy = frustum.fovy || frustum.fov || C.Math.toRadians(60);
13836
14950
  const canvas = this.viewer.scene.canvas;
13837
14951
  const aspectRatio = canvas.width / canvas.height;
13838
- const fovx = 2 * Math.atan(Math.tan(fovy / 2) * aspectRatio);
13839
14952
  const absPitch = Math.abs(pitch);
13840
- const sinPitch = Math.sin(absPitch) || 0.707;
13841
- const altitudeForHeight = rectHeight / 2 / Math.tan(fovy / 2) / sinPitch;
13842
- const altitudeForWidth = rectWidth / 2 / Math.tan(fovx / 2) / sinPitch;
13843
- const altitude = Math.max(altitudeForHeight, altitudeForWidth, 500) * 1.1;
14953
+ const sinPitch = Math.sin(absPitch);
14954
+ const tanHalfFovy = Math.tan(fovy / 2);
14955
+ let viewHeight;
14956
+ if (absPitch > C.Math.toRadians(80)) {
14957
+ const heightForVertical = rectHeight / (2 * tanHalfFovy);
14958
+ const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
14959
+ viewHeight = Math.max(heightForVertical, heightForHorizontal);
14960
+ } else {
14961
+ const heightForVertical = sinPitch > 0.1 ? rectHeight / (2 * tanHalfFovy * sinPitch) : rectHeight / (2 * tanHalfFovy);
14962
+ const heightForHorizontal = rectWidth / (2 * tanHalfFovy * aspectRatio);
14963
+ viewHeight = Math.max(heightForVertical, heightForHorizontal);
14964
+ }
14965
+ viewHeight = Math.max(minHeight, Math.min(maxHeight, viewHeight * 1.2));
14966
+ const altitude = validTerrainHeight + viewHeight;
13844
14967
  const destination = C.Cartesian3.fromDegrees(bounds.centerLon, bounds.centerLat, altitude);
13845
14968
  const orientation = { heading, pitch, roll: 0 };
13846
14969
  if (duration > 0) {
@@ -13848,7 +14971,6 @@ var MassPolygonManager = class {
13848
14971
  } else {
13849
14972
  camera.setView({ destination, orientation });
13850
14973
  }
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
14974
  }
13853
14975
  /**
13854
14976
  * 设置可见性
@@ -13857,7 +14979,12 @@ var MassPolygonManager = class {
13857
14979
  if (this.layerCollection) {
13858
14980
  this.layerCollection.show = visible;
13859
14981
  }
13860
- this.viewer.scene.requestRender();
14982
+ if (this.viewer?.scene) {
14983
+ try {
14984
+ this.viewer.scene.requestRender();
14985
+ } catch (e) {
14986
+ }
14987
+ }
13861
14988
  }
13862
14989
  /**
13863
14990
  * 更新样式(需要重新创建所有多边形)
@@ -13881,6 +15008,10 @@ var MassPolygonManager = class {
13881
15008
  this.clickHandler.destroy();
13882
15009
  this.clickHandler = void 0;
13883
15010
  }
15011
+ if (this.rightClickHandler) {
15012
+ this.rightClickHandler.destroy();
15013
+ this.rightClickHandler = void 0;
15014
+ }
13884
15015
  this.highlightedId = void 0;
13885
15016
  this.selectedId = void 0;
13886
15017
  this.originalHighlightFillColor = void 0;
@@ -13888,20 +15019,32 @@ var MassPolygonManager = class {
13888
15019
  this.originalSelectFillColor = void 0;
13889
15020
  this.originalSelectOutlineColor = void 0;
13890
15021
  this.hoverLabel = void 0;
13891
- if (this.layerCollection) {
13892
- this.viewer.scene.primitives.remove(this.layerCollection);
15022
+ this.highlightPrimitive = void 0;
15023
+ this.selectPrimitive = void 0;
15024
+ if (this.layerCollection && this.viewer?.scene?.primitives) {
15025
+ try {
15026
+ this.viewer.scene.primitives.remove(this.layerCollection);
15027
+ } catch (e) {
15028
+ }
13893
15029
  this.layerCollection = void 0;
13894
15030
  }
13895
15031
  this.primitive = void 0;
13896
15032
  this.outlinePrimitive = void 0;
15033
+ this.groundOutlinePrimitive = void 0;
13897
15034
  this.labelCollection = void 0;
13898
15035
  this.polygonDataMap.clear();
13899
- this.viewer.scene.requestRender();
15036
+ if (this.viewer?.scene) {
15037
+ try {
15038
+ this.viewer.scene.requestRender();
15039
+ } catch (e) {
15040
+ }
15041
+ }
13900
15042
  }
13901
15043
  /**
13902
15044
  * 销毁并释放资源
13903
15045
  */
13904
15046
  destroy() {
15047
+ this.isDestroyed = true;
13905
15048
  this.clear();
13906
15049
  }
13907
15050
  /**
@@ -14295,6 +15438,7 @@ exports.CameraManager = CameraManager;
14295
15438
  exports.Emitter = Emitter;
14296
15439
  exports.FlightSimulator = FlightSimulator;
14297
15440
  exports.FrustumPyramid = FrustumPyramid;
15441
+ exports.ImageryManager = ImageryManager;
14298
15442
  exports.LODManager = LODManager;
14299
15443
  exports.LayerManager = LayerManager;
14300
15444
  exports.MassPolygonManager = MassPolygonManager;
@@ -14305,6 +15449,8 @@ exports.RealtimeFlightTracker = RealtimeFlightTracker;
14305
15449
  exports.SceneManager = SceneManager;
14306
15450
  exports.Selector = Selector;
14307
15451
  exports.StateManager = StateManager;
15452
+ exports.TerrainManager = TerrainManager;
15453
+ exports.TilesetManager = TilesetManager;
14308
15454
  exports.assertCesiumAssetsConfigured = assertCesiumAssetsConfigured;
14309
15455
  exports.bringDataSourceToTop = bringDataSourceToTop;
14310
15456
  exports.bringPrimitiveCollectionToTop = bringPrimitiveCollectionToTop;