@cveo/dsh-digital-earth 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +13 -0
  4. package/README.en.md +124 -0
  5. package/README.md +124 -3
  6. package/client.js +2297 -0
  7. package/cordis.patch.yml +3 -0
  8. package/icon.svg +7 -0
  9. package/index.js +590 -0
  10. package/locale/en.json +6 -0
  11. package/locale/zh.json +6 -0
  12. package/package.json +74 -6
  13. package/preview.png +0 -0
  14. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
  15. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
  16. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
  17. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
  18. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
  19. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
  20. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
  21. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
  22. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
  23. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
  24. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
  25. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
  26. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
  27. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
  28. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
  29. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
  30. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
  31. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
  32. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
  33. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
  34. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
  35. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
  36. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
  37. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
  38. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
  39. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
  40. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
  41. package/vendor/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
  42. package/vendor/cesium/Assets/Images/bing_maps_credit.png +0 -0
  43. package/vendor/cesium/Assets/Images/cesium_credit.png +0 -0
  44. package/vendor/cesium/Assets/Images/google_earth_credit.png +0 -0
  45. package/vendor/cesium/Assets/Images/ion-credit.png +0 -0
  46. package/vendor/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
  47. package/vendor/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
  48. package/vendor/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
  49. package/vendor/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
  50. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
  51. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
  52. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
  53. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
  54. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
  55. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
  56. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
  57. package/vendor/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
  58. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
  59. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
  60. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
  61. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
  62. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
  63. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
  64. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
  65. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
  66. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
  67. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
  68. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
  69. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
  70. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
  71. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
  72. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
  73. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
  74. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
  75. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
  76. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
  77. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
  78. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
  79. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
  80. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
  81. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
  82. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
  83. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
  84. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
  85. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
  86. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
  87. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
  88. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
  89. package/vendor/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
  90. package/vendor/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
  91. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
  92. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
  93. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
  94. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
  95. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
  96. package/vendor/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
  97. package/vendor/cesium/Assets/Textures/maki/airfield.png +0 -0
  98. package/vendor/cesium/Assets/Textures/maki/airport.png +0 -0
  99. package/vendor/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
  100. package/vendor/cesium/Assets/Textures/maki/america-football.png +0 -0
  101. package/vendor/cesium/Assets/Textures/maki/art-gallery.png +0 -0
  102. package/vendor/cesium/Assets/Textures/maki/bakery.png +0 -0
  103. package/vendor/cesium/Assets/Textures/maki/bank.png +0 -0
  104. package/vendor/cesium/Assets/Textures/maki/bar.png +0 -0
  105. package/vendor/cesium/Assets/Textures/maki/baseball.png +0 -0
  106. package/vendor/cesium/Assets/Textures/maki/basketball.png +0 -0
  107. package/vendor/cesium/Assets/Textures/maki/beer.png +0 -0
  108. package/vendor/cesium/Assets/Textures/maki/bicycle.png +0 -0
  109. package/vendor/cesium/Assets/Textures/maki/building.png +0 -0
  110. package/vendor/cesium/Assets/Textures/maki/bus.png +0 -0
  111. package/vendor/cesium/Assets/Textures/maki/cafe.png +0 -0
  112. package/vendor/cesium/Assets/Textures/maki/camera.png +0 -0
  113. package/vendor/cesium/Assets/Textures/maki/campsite.png +0 -0
  114. package/vendor/cesium/Assets/Textures/maki/car.png +0 -0
  115. package/vendor/cesium/Assets/Textures/maki/cemetery.png +0 -0
  116. package/vendor/cesium/Assets/Textures/maki/cesium.png +0 -0
  117. package/vendor/cesium/Assets/Textures/maki/chemist.png +0 -0
  118. package/vendor/cesium/Assets/Textures/maki/cinema.png +0 -0
  119. package/vendor/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
  120. package/vendor/cesium/Assets/Textures/maki/circle.png +0 -0
  121. package/vendor/cesium/Assets/Textures/maki/city.png +0 -0
  122. package/vendor/cesium/Assets/Textures/maki/clothing-store.png +0 -0
  123. package/vendor/cesium/Assets/Textures/maki/college.png +0 -0
  124. package/vendor/cesium/Assets/Textures/maki/commercial.png +0 -0
  125. package/vendor/cesium/Assets/Textures/maki/cricket.png +0 -0
  126. package/vendor/cesium/Assets/Textures/maki/cross.png +0 -0
  127. package/vendor/cesium/Assets/Textures/maki/dam.png +0 -0
  128. package/vendor/cesium/Assets/Textures/maki/danger.png +0 -0
  129. package/vendor/cesium/Assets/Textures/maki/disability.png +0 -0
  130. package/vendor/cesium/Assets/Textures/maki/dog-park.png +0 -0
  131. package/vendor/cesium/Assets/Textures/maki/embassy.png +0 -0
  132. package/vendor/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
  133. package/vendor/cesium/Assets/Textures/maki/entrance.png +0 -0
  134. package/vendor/cesium/Assets/Textures/maki/farm.png +0 -0
  135. package/vendor/cesium/Assets/Textures/maki/fast-food.png +0 -0
  136. package/vendor/cesium/Assets/Textures/maki/ferry.png +0 -0
  137. package/vendor/cesium/Assets/Textures/maki/fire-station.png +0 -0
  138. package/vendor/cesium/Assets/Textures/maki/fuel.png +0 -0
  139. package/vendor/cesium/Assets/Textures/maki/garden.png +0 -0
  140. package/vendor/cesium/Assets/Textures/maki/gift.png +0 -0
  141. package/vendor/cesium/Assets/Textures/maki/golf.png +0 -0
  142. package/vendor/cesium/Assets/Textures/maki/grocery.png +0 -0
  143. package/vendor/cesium/Assets/Textures/maki/hairdresser.png +0 -0
  144. package/vendor/cesium/Assets/Textures/maki/harbor.png +0 -0
  145. package/vendor/cesium/Assets/Textures/maki/heart.png +0 -0
  146. package/vendor/cesium/Assets/Textures/maki/heliport.png +0 -0
  147. package/vendor/cesium/Assets/Textures/maki/hospital.png +0 -0
  148. package/vendor/cesium/Assets/Textures/maki/ice-cream.png +0 -0
  149. package/vendor/cesium/Assets/Textures/maki/industrial.png +0 -0
  150. package/vendor/cesium/Assets/Textures/maki/land-use.png +0 -0
  151. package/vendor/cesium/Assets/Textures/maki/laundry.png +0 -0
  152. package/vendor/cesium/Assets/Textures/maki/library.png +0 -0
  153. package/vendor/cesium/Assets/Textures/maki/lighthouse.png +0 -0
  154. package/vendor/cesium/Assets/Textures/maki/lodging.png +0 -0
  155. package/vendor/cesium/Assets/Textures/maki/logging.png +0 -0
  156. package/vendor/cesium/Assets/Textures/maki/london-underground.png +0 -0
  157. package/vendor/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
  158. package/vendor/cesium/Assets/Textures/maki/marker.png +0 -0
  159. package/vendor/cesium/Assets/Textures/maki/minefield.png +0 -0
  160. package/vendor/cesium/Assets/Textures/maki/mobilephone.png +0 -0
  161. package/vendor/cesium/Assets/Textures/maki/monument.png +0 -0
  162. package/vendor/cesium/Assets/Textures/maki/museum.png +0 -0
  163. package/vendor/cesium/Assets/Textures/maki/music.png +0 -0
  164. package/vendor/cesium/Assets/Textures/maki/oil-well.png +0 -0
  165. package/vendor/cesium/Assets/Textures/maki/park.png +0 -0
  166. package/vendor/cesium/Assets/Textures/maki/park2.png +0 -0
  167. package/vendor/cesium/Assets/Textures/maki/parking-garage.png +0 -0
  168. package/vendor/cesium/Assets/Textures/maki/parking.png +0 -0
  169. package/vendor/cesium/Assets/Textures/maki/pharmacy.png +0 -0
  170. package/vendor/cesium/Assets/Textures/maki/pitch.png +0 -0
  171. package/vendor/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
  172. package/vendor/cesium/Assets/Textures/maki/playground.png +0 -0
  173. package/vendor/cesium/Assets/Textures/maki/police.png +0 -0
  174. package/vendor/cesium/Assets/Textures/maki/polling-place.png +0 -0
  175. package/vendor/cesium/Assets/Textures/maki/post.png +0 -0
  176. package/vendor/cesium/Assets/Textures/maki/prison.png +0 -0
  177. package/vendor/cesium/Assets/Textures/maki/rail-above.png +0 -0
  178. package/vendor/cesium/Assets/Textures/maki/rail-light.png +0 -0
  179. package/vendor/cesium/Assets/Textures/maki/rail-metro.png +0 -0
  180. package/vendor/cesium/Assets/Textures/maki/rail-underground.png +0 -0
  181. package/vendor/cesium/Assets/Textures/maki/rail.png +0 -0
  182. package/vendor/cesium/Assets/Textures/maki/religious-christian.png +0 -0
  183. package/vendor/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
  184. package/vendor/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
  185. package/vendor/cesium/Assets/Textures/maki/restaurant.png +0 -0
  186. package/vendor/cesium/Assets/Textures/maki/roadblock.png +0 -0
  187. package/vendor/cesium/Assets/Textures/maki/rocket.png +0 -0
  188. package/vendor/cesium/Assets/Textures/maki/school.png +0 -0
  189. package/vendor/cesium/Assets/Textures/maki/scooter.png +0 -0
  190. package/vendor/cesium/Assets/Textures/maki/shop.png +0 -0
  191. package/vendor/cesium/Assets/Textures/maki/skiing.png +0 -0
  192. package/vendor/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
  193. package/vendor/cesium/Assets/Textures/maki/soccer.png +0 -0
  194. package/vendor/cesium/Assets/Textures/maki/square-stroked.png +0 -0
  195. package/vendor/cesium/Assets/Textures/maki/square.png +0 -0
  196. package/vendor/cesium/Assets/Textures/maki/star-stroked.png +0 -0
  197. package/vendor/cesium/Assets/Textures/maki/star.png +0 -0
  198. package/vendor/cesium/Assets/Textures/maki/suitcase.png +0 -0
  199. package/vendor/cesium/Assets/Textures/maki/swimming.png +0 -0
  200. package/vendor/cesium/Assets/Textures/maki/telephone.png +0 -0
  201. package/vendor/cesium/Assets/Textures/maki/tennis.png +0 -0
  202. package/vendor/cesium/Assets/Textures/maki/theatre.png +0 -0
  203. package/vendor/cesium/Assets/Textures/maki/toilets.png +0 -0
  204. package/vendor/cesium/Assets/Textures/maki/town-hall.png +0 -0
  205. package/vendor/cesium/Assets/Textures/maki/town.png +0 -0
  206. package/vendor/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
  207. package/vendor/cesium/Assets/Textures/maki/triangle.png +0 -0
  208. package/vendor/cesium/Assets/Textures/maki/village.png +0 -0
  209. package/vendor/cesium/Assets/Textures/maki/warehouse.png +0 -0
  210. package/vendor/cesium/Assets/Textures/maki/waste-basket.png +0 -0
  211. package/vendor/cesium/Assets/Textures/maki/water.png +0 -0
  212. package/vendor/cesium/Assets/Textures/maki/wetland.png +0 -0
  213. package/vendor/cesium/Assets/Textures/maki/zoo.png +0 -0
  214. package/vendor/cesium/Assets/Textures/moonSmall.jpg +0 -0
  215. package/vendor/cesium/Assets/Textures/pin.svg +1 -0
  216. package/vendor/cesium/Assets/Textures/waterNormals.jpg +0 -0
  217. package/vendor/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
  218. package/vendor/cesium/Assets/approximateTerrainHeights.json +1 -0
  219. package/vendor/cesium/Cesium.js +18348 -0
  220. package/vendor/cesium/LICENSE.md +1068 -0
  221. package/vendor/cesium/ThirdParty/Workers/package.json +1 -0
  222. package/vendor/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
  223. package/vendor/cesium/ThirdParty/basis_transcoder.wasm +0 -0
  224. package/vendor/cesium/ThirdParty/draco_decoder.wasm +0 -0
  225. package/vendor/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
  226. package/vendor/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
  227. package/vendor/cesium/ThirdParty/zip-module.wasm +0 -0
  228. package/vendor/cesium/Widgets/Animation/Animation.css +127 -0
  229. package/vendor/cesium/Widgets/Animation/lighter.css +70 -0
  230. package/vendor/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
  231. package/vendor/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
  232. package/vendor/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
  233. package/vendor/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
  234. package/vendor/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
  235. package/vendor/cesium/Widgets/CesiumWidget/lighter.css +14 -0
  236. package/vendor/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
  237. package/vendor/cesium/Widgets/Geocoder/Geocoder.css +70 -0
  238. package/vendor/cesium/Widgets/Geocoder/lighter.css +17 -0
  239. package/vendor/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
  240. package/vendor/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
  241. package/vendor/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
  242. package/vendor/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
  243. package/vendor/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
  244. package/vendor/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
  245. package/vendor/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
  246. package/vendor/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
  247. package/vendor/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
  248. package/vendor/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
  249. package/vendor/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
  250. package/vendor/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
  251. package/vendor/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
  252. package/vendor/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
  253. package/vendor/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
  254. package/vendor/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
  255. package/vendor/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
  256. package/vendor/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
  257. package/vendor/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
  258. package/vendor/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
  259. package/vendor/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
  260. package/vendor/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
  261. package/vendor/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
  262. package/vendor/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
  263. package/vendor/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
  264. package/vendor/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
  265. package/vendor/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
  266. package/vendor/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
  267. package/vendor/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
  268. package/vendor/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
  269. package/vendor/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
  270. package/vendor/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
  271. package/vendor/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
  272. package/vendor/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
  273. package/vendor/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
  274. package/vendor/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
  275. package/vendor/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
  276. package/vendor/cesium/Widgets/Images/TimelineIcons.png +0 -0
  277. package/vendor/cesium/Widgets/Images/info-loading.gif +0 -0
  278. package/vendor/cesium/Widgets/InfoBox/InfoBox.css +92 -0
  279. package/vendor/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
  280. package/vendor/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
  281. package/vendor/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
  282. package/vendor/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
  283. package/vendor/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
  284. package/vendor/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
  285. package/vendor/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
  286. package/vendor/cesium/Widgets/Timeline/Timeline.css +103 -0
  287. package/vendor/cesium/Widgets/Timeline/lighter.css +23 -0
  288. package/vendor/cesium/Widgets/VRButton/VRButton.css +8 -0
  289. package/vendor/cesium/Widgets/Viewer/Viewer.css +107 -0
  290. package/vendor/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
  291. package/vendor/cesium/Widgets/lighter.css +237 -0
  292. package/vendor/cesium/Widgets/lighterShared.css +46 -0
  293. package/vendor/cesium/Widgets/shared.css +103 -0
  294. package/vendor/cesium/Widgets/widgets.css +1342 -0
  295. package/vendor/cesium/Workers/chunk-3XZ3CL24.js +26 -0
  296. package/vendor/cesium/Workers/chunk-674HC7OA.js +26 -0
  297. package/vendor/cesium/Workers/chunk-6LO6MWK3.js +26 -0
  298. package/vendor/cesium/Workers/chunk-F6M5ZWO4.js +29 -0
  299. package/vendor/cesium/Workers/chunk-FZYUOFLY.js +70 -0
  300. package/vendor/cesium/Workers/chunk-GACOCTFW.js +26 -0
  301. package/vendor/cesium/Workers/chunk-MDI2T7LJ.js +27 -0
  302. package/vendor/cesium/Workers/chunk-P6X455E3.js +27 -0
  303. package/vendor/cesium/Workers/chunk-SOIWLLGP.js +26 -0
  304. package/vendor/cesium/Workers/chunk-UBKFZE5C.js +26 -0
  305. package/vendor/cesium/Workers/combineGeometry.js +26 -0
  306. package/vendor/cesium/Workers/createBoxGeometry.js +26 -0
  307. package/vendor/cesium/Workers/createBoxOutlineGeometry.js +26 -0
  308. package/vendor/cesium/Workers/createCircleGeometry.js +26 -0
  309. package/vendor/cesium/Workers/createCircleOutlineGeometry.js +26 -0
  310. package/vendor/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
  311. package/vendor/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
  312. package/vendor/cesium/Workers/createCorridorGeometry.js +26 -0
  313. package/vendor/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
  314. package/vendor/cesium/Workers/createCylinderGeometry.js +26 -0
  315. package/vendor/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
  316. package/vendor/cesium/Workers/createEllipseGeometry.js +26 -0
  317. package/vendor/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
  318. package/vendor/cesium/Workers/createEllipsoidGeometry.js +26 -0
  319. package/vendor/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
  320. package/vendor/cesium/Workers/createFrustumGeometry.js +26 -0
  321. package/vendor/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
  322. package/vendor/cesium/Workers/createGeometry.js +26 -0
  323. package/vendor/cesium/Workers/createGroundPolylineGeometry.js +26 -0
  324. package/vendor/cesium/Workers/createPlaneGeometry.js +26 -0
  325. package/vendor/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
  326. package/vendor/cesium/Workers/createPolygonGeometry.js +26 -0
  327. package/vendor/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
  328. package/vendor/cesium/Workers/createPolylineGeometry.js +26 -0
  329. package/vendor/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
  330. package/vendor/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
  331. package/vendor/cesium/Workers/createRectangleGeometry.js +26 -0
  332. package/vendor/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
  333. package/vendor/cesium/Workers/createSimplePolylineGeometry.js +26 -0
  334. package/vendor/cesium/Workers/createSphereGeometry.js +26 -0
  335. package/vendor/cesium/Workers/createSphereOutlineGeometry.js +26 -0
  336. package/vendor/cesium/Workers/createTaskProcessorWorker.js +26 -0
  337. package/vendor/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
  338. package/vendor/cesium/Workers/createVectorTileGeometries.js +26 -0
  339. package/vendor/cesium/Workers/createVectorTilePoints.js +26 -0
  340. package/vendor/cesium/Workers/createVectorTilePolygons.js +26 -0
  341. package/vendor/cesium/Workers/createVectorTilePolylines.js +26 -0
  342. package/vendor/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
  343. package/vendor/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
  344. package/vendor/cesium/Workers/createVerticesFromHeightmap.js +26 -0
  345. package/vendor/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
  346. package/vendor/cesium/Workers/createWallGeometry.js +26 -0
  347. package/vendor/cesium/Workers/createWallOutlineGeometry.js +26 -0
  348. package/vendor/cesium/Workers/decodeDraco.js +26 -0
  349. package/vendor/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
  350. package/vendor/cesium/Workers/decodeI3S.js +26 -0
  351. package/vendor/cesium/Workers/gaussianSplatSorter.js +26 -0
  352. package/vendor/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
  353. package/vendor/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
  354. package/vendor/cesium/Workers/transcodeKTX2.js +56 -0
  355. package/vendor/cesium/Workers/transferTypedArrayTest.js +26 -0
  356. package/vendor/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
  357. package/vendor/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
  358. package/vendor/cesium/index.cjs +18348 -0
  359. package/vendor/cesium/index.js +18348 -0
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-digital-earth
3
+ name: '@cveo/dsh-digital-earth'
package/icon.svg ADDED
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" fill="none">
2
+ <circle cx="30" cy="30" r="20" stroke="#4f8cff" stroke-width="3"/>
3
+ <ellipse cx="30" cy="30" rx="8.5" ry="20" stroke="#4f8cff" stroke-width="2.2" opacity=".85"/>
4
+ <path d="M10.5 30h39M13.5 20.5h33M13.5 39.5h33" stroke="#4f8cff" stroke-width="2" opacity=".6"/>
5
+ <path d="M48 34l9 5v10l-9 5-9-5V39z" fill="#4f8cff"/>
6
+ <path d="M39 39l9 5 9-5M48 44v10" stroke="#eaf2ff" stroke-width="1.6" opacity=".9"/>
7
+ </svg>
package/index.js ADDED
@@ -0,0 +1,590 @@
1
+ /**
2
+ * Host half of the 3D viewer plugin.
3
+ *
4
+ * Owns everything the browser must not decide on its own:
5
+ * - a per-process token, so only this UI page can read local files;
6
+ * - same-origin static routes for the vendored CesiumJS build and for the
7
+ * tileset files under a directory the user picked in the browser;
8
+ * - a directory scan for `tileset.json` and a small CORS-bypass proxy for
9
+ * online tilesets whose server does not send CORS headers.
10
+ */
11
+ import { randomBytes } from 'node:crypto'
12
+ import { createReadStream, promises as fs } from 'node:fs'
13
+ import { homedir } from 'node:os'
14
+ import { extname, join, resolve, sep } from 'node:path'
15
+ import { Readable } from 'node:stream'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ const ROOT_DIR = fileURLToPath(new URL('.', import.meta.url))
19
+ const CESIUM_DIR = join(ROOT_DIR, 'vendor', 'cesium')
20
+ const TOKEN = randomBytes(12).toString('base64url')
21
+ const API = '/api/3dviewer'
22
+ // Prefix routes are registered without the trailing slash: the router matches
23
+ // `pathname === prefix || pathname.startsWith(prefix + '/')`.
24
+ const CESIUM_ROUTE = `${API}/cesium`
25
+ const TILES_ROUTE = `${API}/tiles`
26
+ const REMOTE_ROUTE = `${API}/remote`
27
+ const SOURCES_ROUTE = `${API}/sources`
28
+ const MAX_SCAN_ITEMS = 200
29
+ const MAX_SCAN_DEPTH = 4
30
+ // The source catalog is small JSON owned by this plugin; keep it outside the
31
+ // browser so it survives port changes (localStorage is per-origin) and restarts.
32
+ const STATE_DIR = join(process.env.DSH_HOME || join(homedir(), '.dsh'), 'digital-earth')
33
+ const SOURCES_FILE = join(STATE_DIR, 'sources.json')
34
+ const MAX_SOURCES_BODY = 128 * 1024
35
+ const MAX_IMAGERY_SOURCES = 20
36
+ const MAX_TERRAIN_SOURCES = 10
37
+ const IMAGERY_TYPES = new Set(['xyz', 'arcgis', 'wms'])
38
+ const TERRAIN_TYPES = new Set(['arcgis', 'cesium'])
39
+ const THEME_MODES = new Set(['auto', 'light', 'dark'])
40
+
41
+ /**
42
+ * Built-in catalogs, used for a first run and for the "restore defaults"
43
+ * action. Names are brand names rather than translated UI copy, so the same
44
+ * value reads correctly in both locales.
45
+ */
46
+ const DEFAULT_SOURCES = {
47
+ imagery: [
48
+ {
49
+ id: 'esri-world-imagery',
50
+ name: 'Esri World Imagery',
51
+ type: 'xyz',
52
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
53
+ maximumLevel: 19,
54
+ credit: 'Esri, Maxar, Earthstar Geographics, and the GIS User Community',
55
+ },
56
+ {
57
+ id: 'esri-world-topo',
58
+ name: 'Esri World Topographic',
59
+ type: 'arcgis',
60
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer',
61
+ credit: 'Esri, HERE, Garmin, FAO, NOAA, USGS, and the GIS User Community',
62
+ },
63
+ {
64
+ id: 'esri-world-street',
65
+ name: 'Esri World Street Map',
66
+ type: 'arcgis',
67
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer',
68
+ credit: 'Esri, HERE, Garmin, USGS, NGA, and the GIS User Community',
69
+ },
70
+ {
71
+ id: 'esri-light-gray',
72
+ name: 'Esri Light Gray Canvas',
73
+ type: 'arcgis',
74
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer',
75
+ credit: 'Esri, HERE, Garmin, and the GIS User Community',
76
+ },
77
+ {
78
+ id: 'esri-shaded-relief',
79
+ name: 'Esri World Shaded Relief',
80
+ type: 'arcgis',
81
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer',
82
+ credit: 'Esri, and the GIS User Community',
83
+ },
84
+ {
85
+ id: 'esri-world-ocean',
86
+ name: 'Esri World Ocean',
87
+ type: 'arcgis',
88
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Ocean/World_Ocean_Base/MapServer',
89
+ credit: 'Esri, GEBCO, NOAA, and the GIS User Community',
90
+ },
91
+ {
92
+ id: 'esri-world-hillshade',
93
+ name: 'Esri World Hillshade',
94
+ type: 'arcgis',
95
+ url: 'https://server.arcgisonline.com/ArcGIS/rest/services/Elevation/World_Hillshade/MapServer',
96
+ credit: 'Esri, USGS, and the GIS User Community',
97
+ },
98
+ ],
99
+ terrain: [
100
+ {
101
+ id: 'arcgis-world-elevation',
102
+ name: 'ArcGIS WorldElevation3D',
103
+ type: 'arcgis',
104
+ url: 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
105
+ credit: 'Esri, CGIAR, USGS',
106
+ },
107
+ ],
108
+ settings: { theme: 'auto', imagery: 'esri-world-imagery', terrain: null },
109
+ }
110
+
111
+ const MIME = {
112
+ '.json': 'application/json; charset=utf-8',
113
+ '.js': 'text/javascript; charset=utf-8',
114
+ '.mjs': 'text/javascript; charset=utf-8',
115
+ '.css': 'text/css; charset=utf-8',
116
+ '.html': 'text/html; charset=utf-8',
117
+ '.txt': 'text/plain; charset=utf-8',
118
+ '.svg': 'image/svg+xml',
119
+ '.png': 'image/png',
120
+ '.jpg': 'image/jpeg',
121
+ '.jpeg': 'image/jpeg',
122
+ '.webp': 'image/webp',
123
+ '.gif': 'image/gif',
124
+ '.glb': 'model/gltf-binary',
125
+ '.gltf': 'model/gltf+json',
126
+ '.bin': 'application/octet-stream',
127
+ '.ktx2': 'image/ktx2',
128
+ '.wasm': 'application/wasm',
129
+ '.terrain': 'application/octet-stream',
130
+ '.b3dm': 'application/octet-stream',
131
+ '.pnts': 'application/octet-stream',
132
+ '.i3dm': 'application/octet-stream',
133
+ '.cmpt': 'application/octet-stream',
134
+ '.spz': 'application/octet-stream',
135
+ }
136
+
137
+ const toB64 = (value) => Buffer.from(value, 'utf8').toString('base64url')
138
+ const fromB64 = (value) => Buffer.from(value, 'base64url').toString('utf8')
139
+ const segments = (value) => value.split('/').filter((part) => part.length > 0)
140
+ const decodeSegment = (value) => {
141
+ try {
142
+ return decodeURIComponent(value)
143
+ } catch {
144
+ return null
145
+ }
146
+ }
147
+
148
+ function sendText(res, status, text, type = 'text/plain; charset=utf-8') {
149
+ res.writeHead(status, { 'content-type': type, 'cache-control': 'no-store' })
150
+ res.end(text)
151
+ }
152
+
153
+ function sendJson(res, status, data) {
154
+ res.writeHead(status, {
155
+ 'content-type': 'application/json; charset=utf-8',
156
+ 'cache-control': 'no-store',
157
+ })
158
+ res.end(JSON.stringify(data))
159
+ }
160
+
161
+ const cloneDefaults = () => JSON.parse(JSON.stringify(DEFAULT_SOURCES))
162
+
163
+ const cleanText = (value, max) => (typeof value === 'string' ? value.trim().slice(0, max) : '')
164
+
165
+ /**
166
+ * Accept only absolute http(s) URLs. Imagery templates legitimately contain
167
+ * placeholders such as `{z}` or `{s}`, so those are neutralised before parsing
168
+ * and the original text is what gets stored.
169
+ */
170
+ function cleanUrl(value) {
171
+ const text = cleanText(value, 2048)
172
+ if (text === '') return ''
173
+ try {
174
+ const parsed = new URL(text.replace(/\{[^}]*\}/g, '0'))
175
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? text : ''
176
+ } catch {
177
+ return ''
178
+ }
179
+ }
180
+
181
+ function cleanId(value, fallback) {
182
+ const text = typeof value === 'string' ? value.trim().toLowerCase() : ''
183
+ return /^[a-z0-9][a-z0-9-]{0,39}$/.test(text) ? text : fallback
184
+ }
185
+
186
+ function cleanLevel(value) {
187
+ return Number.isInteger(value) && value >= 0 && value <= 25 ? value : undefined
188
+ }
189
+
190
+ function normalizeSource(kind, raw, index) {
191
+ if (raw === null || typeof raw !== 'object') return null
192
+ const type = cleanText(raw.type, 16).toLowerCase()
193
+ if (!(kind === 'imagery' ? IMAGERY_TYPES : TERRAIN_TYPES).has(type)) return null
194
+ const url = cleanUrl(raw.url)
195
+ if (url === '') return null
196
+ const source = {
197
+ id: cleanId(raw.id, `${kind}-${index + 1}`),
198
+ name: cleanText(raw.name, 60) || `${kind} ${index + 1}`,
199
+ type,
200
+ url,
201
+ }
202
+ const credit = cleanText(raw.credit, 200)
203
+ if (credit !== '') source.credit = credit
204
+ if (kind === 'imagery') {
205
+ const maximumLevel = cleanLevel(raw.maximumLevel)
206
+ if (maximumLevel !== undefined) source.maximumLevel = maximumLevel
207
+ if (raw.tilingScheme === 'geographic' || raw.tilingScheme === 'web-mercator') source.tilingScheme = raw.tilingScheme
208
+ if (raw.reverseY === true) source.reverseY = true
209
+ const subdomains = Array.isArray(raw.subdomains)
210
+ ? raw.subdomains.map((item) => cleanText(item, 8)).filter((item) => item !== '').slice(0, 8)
211
+ : []
212
+ if (subdomains.length > 0) source.subdomains = subdomains
213
+ const layers = cleanText(raw.layers, 200)
214
+ if (layers !== '') source.layers = layers
215
+ } else {
216
+ if (raw.requestVertexNormals === true) source.requestVertexNormals = true
217
+ if (raw.requestWaterMask === true) source.requestWaterMask = true
218
+ }
219
+ return source
220
+ }
221
+
222
+ function normalizeSourceList(kind, raw, fallback) {
223
+ if (!Array.isArray(raw)) return fallback.map((source) => ({ ...source }))
224
+ const limit = kind === 'imagery' ? MAX_IMAGERY_SOURCES : MAX_TERRAIN_SOURCES
225
+ const seen = new Set()
226
+ const sources = []
227
+ for (const [index, item] of raw.slice(0, limit).entries()) {
228
+ const source = normalizeSource(kind, item, index)
229
+ if (source === null) continue
230
+ while (seen.has(source.id)) source.id = `${source.id}-${sources.length + 1}`
231
+ seen.add(source.id)
232
+ sources.push(source)
233
+ }
234
+ return sources
235
+ }
236
+
237
+ /** Pick a stored selection id, but only when it still exists in the list. */
238
+ function cleanSelection(value, sources) {
239
+ if (typeof value !== 'string') return null
240
+ return sources.some((source) => source.id === value) ? value : null
241
+ }
242
+
243
+ /** Append built-in presets that the user has not seen yet (id-based, keeps order). */
244
+ function withBuiltins(list, builtins, limit) {
245
+ const seen = new Set(list.map((source) => source.id))
246
+ const merged = [...list]
247
+ for (const source of builtins) {
248
+ if (merged.length >= limit) break
249
+ if (seen.has(source.id)) continue
250
+ merged.push({ ...source })
251
+ }
252
+ return merged
253
+ }
254
+
255
+ /** Turn arbitrary request JSON into the catalog persisted on disk. */
256
+ function normalizeConfig(raw) {
257
+ const input = raw !== null && typeof raw === 'object' ? raw : {}
258
+ const theme = THEME_MODES.has(input.settings?.theme) ? input.settings.theme : 'auto'
259
+ const imagery = withBuiltins(
260
+ normalizeSourceList('imagery', input.imagery, DEFAULT_SOURCES.imagery),
261
+ DEFAULT_SOURCES.imagery,
262
+ MAX_IMAGERY_SOURCES,
263
+ )
264
+ const terrain = withBuiltins(
265
+ normalizeSourceList('terrain', input.terrain, DEFAULT_SOURCES.terrain),
266
+ DEFAULT_SOURCES.terrain,
267
+ MAX_TERRAIN_SOURCES,
268
+ )
269
+ // A missing selection (older file, first run) falls back to the default choice;
270
+ // an explicit `null` means "basemap/terrain deliberately off" and is preserved.
271
+ const selection = (value, list, fallback) =>
272
+ value === undefined ? cleanSelection(fallback, list) : cleanSelection(value, list)
273
+ return {
274
+ imagery,
275
+ terrain,
276
+ settings: {
277
+ theme,
278
+ imagery: selection(input.settings?.imagery, imagery, DEFAULT_SOURCES.settings.imagery),
279
+ terrain: selection(input.settings?.terrain, terrain, DEFAULT_SOURCES.settings.terrain),
280
+ },
281
+ }
282
+ }
283
+
284
+ async function readSources() {
285
+ let text
286
+ try {
287
+ text = await fs.readFile(SOURCES_FILE, 'utf8')
288
+ } catch {
289
+ return cloneDefaults()
290
+ }
291
+ try {
292
+ return normalizeConfig(JSON.parse(text))
293
+ } catch {
294
+ return cloneDefaults()
295
+ }
296
+ }
297
+
298
+ async function writeSources(config) {
299
+ await fs.mkdir(STATE_DIR, { recursive: true })
300
+ const temporary = `${SOURCES_FILE}.tmp`
301
+ await fs.writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
302
+ await fs.rename(temporary, SOURCES_FILE)
303
+ }
304
+
305
+ /** Hosts the proxy must never reach: loopback, private ranges and link-local. */
306
+ function blockedHost(origin) {
307
+ let hostname
308
+ try {
309
+ hostname = new URL(origin).hostname.toLowerCase()
310
+ } catch {
311
+ return true
312
+ }
313
+ if (hostname.startsWith('[')) return true
314
+ if (hostname === 'localhost' || hostname.endsWith('.localhost')) return true
315
+ if (hostname.endsWith('.local') || hostname.endsWith('.internal')) return true
316
+ const parts = hostname.split('.').map((part) => Number(part))
317
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part))) return false
318
+ const [a, b] = parts
319
+ return a === 0 || a === 10 || a === 127 || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || a >= 224
320
+ }
321
+
322
+ function within(root, target) {
323
+ const prefix = root.endsWith(sep) ? root : root + sep
324
+ return target === root || target.startsWith(prefix)
325
+ }
326
+
327
+ /** Percent-encoded pathname of a request; the router hands route handlers only (req, res). */
328
+ function requestPath(req) {
329
+ try {
330
+ return new URL(req.url ?? '/', 'http://localhost').pathname
331
+ } catch {
332
+ return '/'
333
+ }
334
+ }
335
+
336
+ /** The raw path below a prefix route, or `null` when the request is not below it. */
337
+ function subPath(pathname, prefix) {
338
+ if (pathname === prefix) return ''
339
+ if (pathname.startsWith(`${prefix}/`)) return pathname.slice(prefix.length + 1)
340
+ return null
341
+ }
342
+
343
+ async function serveFile(req, res, root, relPath, cacheControl) {
344
+ if (relPath === '') return sendText(res, 404, 'not found')
345
+ const target = resolve(root, relPath)
346
+ if (!within(root, target)) return sendText(res, 403, 'forbidden')
347
+ let stat
348
+ try {
349
+ stat = await fs.stat(target)
350
+ } catch {
351
+ return sendText(res, 404, 'not found')
352
+ }
353
+ if (!stat.isFile()) return sendText(res, 404, 'not found')
354
+ res.writeHead(200, {
355
+ 'content-type': MIME[extname(target).toLowerCase()] ?? 'application/octet-stream',
356
+ 'content-length': String(stat.size),
357
+ 'cache-control': cacheControl,
358
+ })
359
+ if (req.method === 'HEAD') {
360
+ res.end()
361
+ return
362
+ }
363
+ createReadStream(target)
364
+ .on('error', () => res.destroy())
365
+ .pipe(res)
366
+ }
367
+
368
+ function metaHandler(req, res) {
369
+ sendJson(res, 200, {
370
+ token: TOKEN,
371
+ cesiumBase: `${API}/cesium/`,
372
+ tilesBase: `${API}/tiles/${TOKEN}/`,
373
+ scanUrl: `${API}/scan`,
374
+ remoteBase: `${API}/remote/${TOKEN}/`,
375
+ sourcesUrl: `${SOURCES_ROUTE}/${TOKEN}`,
376
+ })
377
+ }
378
+
379
+ /** GET: the current catalog plus the built-ins the UI can restore. */
380
+ async function sourcesHandler(req, res) {
381
+ sendJson(res, 200, { config: await readSources(), defaults: cloneDefaults() })
382
+ }
383
+
384
+ /** PUT: replace the catalog, normalised so a malformed entry cannot reach Cesium. */
385
+ async function saveSourcesHandler(req, res) {
386
+ let body = ''
387
+ try {
388
+ for await (const chunk of req) {
389
+ body += chunk
390
+ if (body.length > MAX_SOURCES_BODY) {
391
+ req.destroy()
392
+ return sendText(res, 413, 'payload too large')
393
+ }
394
+ }
395
+ } catch {
396
+ return sendText(res, 400, 'bad request')
397
+ }
398
+ let parsed
399
+ try {
400
+ parsed = JSON.parse(body)
401
+ } catch {
402
+ return sendText(res, 400, 'invalid json')
403
+ }
404
+ const config = normalizeConfig(parsed)
405
+ try {
406
+ await writeSources(config)
407
+ } catch (error) {
408
+ return sendText(res, 500, `save failed: ${error instanceof Error ? error.message : String(error)}`)
409
+ }
410
+ sendJson(res, 200, { config })
411
+ }
412
+
413
+ function cesiumHandler(req, res) {
414
+ const raw = subPath(requestPath(req), CESIUM_ROUTE)
415
+ if (raw === null || raw === '') return sendText(res, 404, 'not found')
416
+ const relPath = decodeSegment(raw)
417
+ if (relPath === null) return sendText(res, 400, 'bad path')
418
+ serveFile(req, res, resolve(CESIUM_DIR), relPath, 'public, max-age=86400')
419
+ }
420
+
421
+ function tilesHandler(req, res, allowedDirs) {
422
+ const raw = subPath(requestPath(req), TILES_ROUTE)
423
+ if (raw === null) return sendText(res, 403, 'forbidden')
424
+ const parts = segments(raw)
425
+ if (parts[0] !== TOKEN || parts.length < 2) return sendText(res, 403, 'forbidden')
426
+ const dir = fromB64(parts[1])
427
+ // Only absolute Windows or POSIX directories, and only ones the user opened.
428
+ if (!/^([a-zA-Z]:[\\/]|\\\\|\/)/.test(dir) || !allowedDirs.has(resolve(dir))) {
429
+ return sendText(res, 403, 'forbidden')
430
+ }
431
+ const relParts = []
432
+ for (const part of parts.slice(2)) {
433
+ const decoded = decodeSegment(part)
434
+ if (decoded === null || decoded.includes('\0') || decoded === '..') {
435
+ return sendText(res, 403, 'forbidden')
436
+ }
437
+ relParts.push(decoded)
438
+ }
439
+ serveFile(req, res, resolve(dir), relParts.join(sep), 'public, max-age=60')
440
+ }
441
+
442
+ async function scanHandler(req, res, allowedDirs) {
443
+ let parsed
444
+ try {
445
+ parsed = new URL(req.url, 'http://localhost')
446
+ } catch {
447
+ return sendText(res, 400, 'bad request')
448
+ }
449
+ const dir = parsed.searchParams.get('dir')
450
+ if (parsed.searchParams.get('token') !== TOKEN || !dir) return sendText(res, 403, 'forbidden')
451
+ allowedDirs.add(resolve(dir))
452
+ const files = await findTilesets(dir)
453
+ const items = files.map((file) => {
454
+ const rel = file
455
+ .slice(dir.length)
456
+ .replace(/^[\\/]+/, '')
457
+ .split(/[\\/]/)
458
+ .map(encodeURIComponent)
459
+ .join('/')
460
+ return {
461
+ name: file.slice(dir.length).replace(/^[\\/]+/, ''),
462
+ url: `${API}/tiles/${TOKEN}/${encodeURIComponent(toB64(dir))}/${rel}`,
463
+ }
464
+ })
465
+ sendJson(res, 200, { items })
466
+ }
467
+
468
+ async function findTilesets(dir) {
469
+ const found = []
470
+ async function walk(current, depth) {
471
+ if (found.length >= MAX_SCAN_ITEMS) return
472
+ let entries
473
+ try {
474
+ entries = await fs.readdir(current, { withFileTypes: true })
475
+ } catch {
476
+ return
477
+ }
478
+ for (const entry of entries) {
479
+ if (found.length >= MAX_SCAN_ITEMS) return
480
+ const full = join(current, entry.name)
481
+ if (entry.isDirectory()) {
482
+ if (depth < MAX_SCAN_DEPTH) await walk(full, depth + 1)
483
+ } else if (entry.name.toLowerCase() === 'tileset.json') {
484
+ found.push(full)
485
+ }
486
+ }
487
+ }
488
+ await walk(dir, 0)
489
+ return found
490
+ }
491
+
492
+ async function remoteHandler(req, res) {
493
+ const raw = subPath(requestPath(req), REMOTE_ROUTE)
494
+ if (raw === null) return sendText(res, 403, 'forbidden')
495
+ const parts = segments(raw)
496
+ if (parts[0] !== TOKEN || parts.length < 2) return sendText(res, 403, 'forbidden')
497
+ const origin = fromB64(parts[1])
498
+ if (!/^https?:\/\/[^/]+$/i.test(origin) || blockedHost(origin)) return sendText(res, 403, 'forbidden')
499
+ const relParts = []
500
+ for (const part of parts.slice(2)) {
501
+ const decoded = decodeSegment(part)
502
+ if (decoded === null || decoded.includes('\0')) return sendText(res, 403, 'forbidden')
503
+ relParts.push(decoded)
504
+ }
505
+ const target = `${origin}/${relParts.join('/')}`
506
+ try {
507
+ const upstream = await fetch(target, { redirect: 'follow' })
508
+ if (!upstream.ok || upstream.body === null) {
509
+ return sendText(res, upstream.status === 200 ? 502 : upstream.status, `upstream ${upstream.status}`)
510
+ }
511
+ res.writeHead(200, {
512
+ 'content-type': upstream.headers.get('content-type') ?? 'application/octet-stream',
513
+ 'cache-control': 'no-store',
514
+ })
515
+ Readable.fromWeb(upstream.body).pipe(res)
516
+ } catch (error) {
517
+ sendText(res, 502, `proxy failed: ${error instanceof Error ? error.message : String(error)}`)
518
+ }
519
+ }
520
+
521
+ export const inject = ['webServer', 'connection']
522
+
523
+ export function apply(ctx) {
524
+ // Directories this page opened through `/scan`; tile files are served only from
525
+ // them, so a token holder cannot read arbitrary paths on the machine.
526
+ const allowedDirs = new Set()
527
+
528
+ // The host web server has no auth or origin policy of its own, so every route
529
+ // below asks the DSH Connection service to reject foreign or unauthenticated
530
+ // requests, and fails closed whenever that answer is doubtful.
531
+ const rejection = (req) => {
532
+ const headers = req.headers ?? {}
533
+ const site = headers['sec-fetch-site']
534
+ if (site === 'cross-site') return 403
535
+ const origin = headers.origin
536
+ if (typeof origin === 'string' && origin !== 'null' && headers.host) {
537
+ try {
538
+ if (new URL(origin).host !== headers.host) return 403
539
+ } catch {
540
+ return 403
541
+ }
542
+ }
543
+ let connection
544
+ try {
545
+ connection = ctx.get('connection') ?? ctx.connection
546
+ } catch {
547
+ connection = null
548
+ }
549
+ if (connection && typeof connection.requestRejection === 'function') {
550
+ try {
551
+ const code = connection.requestRejection(req)
552
+ return code === undefined || code === null || code === false ? undefined : typeof code === 'number' ? code : 403
553
+ } catch {
554
+ return 403
555
+ }
556
+ }
557
+ // Older hosts without that fence: only this page's own requests may pass.
558
+ return site === 'same-origin' || site === 'none' ? undefined : 403
559
+ }
560
+
561
+ const register = (kind, path, handler) =>
562
+ ctx.webServer.register({
563
+ kind,
564
+ path,
565
+ handler(req, res) {
566
+ const code = rejection(req)
567
+ if (code) return sendText(res, code, 'forbidden')
568
+ return handler(req, res)
569
+ },
570
+ })
571
+
572
+ ctx.effect(() => {
573
+ const disposers = [
574
+ register('exact', `${API}/meta`, metaHandler),
575
+ register('prefix', CESIUM_ROUTE, cesiumHandler),
576
+ register('prefix', TILES_ROUTE, (req, res) => tilesHandler(req, res, allowedDirs)),
577
+ register('prefix', REMOTE_ROUTE, remoteHandler),
578
+ register('exact', `${API}/scan`, (req, res) => scanHandler(req, res, allowedDirs)),
579
+ register('exact', `${SOURCES_ROUTE}/${TOKEN}`, (req, res) =>
580
+ req.method === 'GET' ? sourcesHandler(req, res) : req.method === 'PUT' ? saveSourcesHandler(req, res) : sendText(res, 405, 'method not allowed'),
581
+ ),
582
+ ]
583
+ return () => {
584
+ for (const dispose of disposers) {
585
+ if (typeof dispose === 'function') dispose()
586
+ }
587
+ }
588
+ }, 'digital-earth api routes')
589
+ }
590
+
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Digital Earth",
4
+ "description": "Lay 3D Tiles / 3DGS layers over a CesiumJS globe, with bilingual UI, light/dark themes, managed imagery and terrain sources, a coordinate HUD and measurement tools."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "数字地球",
4
+ "description": "在 CesiumJS 地球上叠加 3D Tiles / 3DGS 图层,内置双语界面、浅深主题、影像与地形数据源管理、坐标 HUD 与量测工具。"
5
+ }
6
+ }