bruce-cesium 7.1.6 → 7.1.7

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.
@@ -2668,185 +2668,909 @@
2668
2668
  CesiumEntityStyler.Unhighlight = Unhighlight;
2669
2669
  })(exports.CesiumEntityStyler || (exports.CesiumEntityStyler = {}));
2670
2670
 
2671
- (function (Widget) {
2672
- class AWidget {
2673
- get WidgetId() {
2674
- return this.widgetId;
2675
- }
2676
- get Viewer() {
2677
- return this._viewer;
2678
- }
2679
- get Container() {
2680
- return this._container;
2681
- }
2682
- get Element() {
2683
- return this._element;
2671
+ function OnNextCRender(viewer, callback) {
2672
+ const removal = viewer.scene.postRender.addEventListener(() => {
2673
+ removal();
2674
+ callback();
2675
+ });
2676
+ }
2677
+ function GetCValue(viewer, obj) {
2678
+ if (obj === null || obj === void 0 ? void 0 : obj.getValue) {
2679
+ let date = viewer.scene.lastRenderTime;
2680
+ if (!date) {
2681
+ date = viewer.clock.currentTime;
2684
2682
  }
2685
- constructor(params) {
2686
- this.widgetId = BModels.ObjectUtils.UId();
2687
- let { viewer, container, manager } = params;
2688
- // A host app can pass an undefined viewer (eg. mid-teardown); fall back to
2689
- // no container rather than crashing on viewer.container.
2690
- if (!container && viewer) {
2691
- container = viewer.container;
2692
- }
2693
- this._viewer = viewer;
2694
- this._manager = manager;
2695
- this._container = container;
2696
- this._apiGetters = params.apiGetters;
2697
- if (viewer) {
2698
- markViewerWithWidget(viewer, this.widgetId, this);
2699
- }
2683
+ return obj.getValue(date);
2684
+ }
2685
+ return obj;
2686
+ }
2687
+ /*
2688
+ * Marks an EntityCollection as one this library created and fully controls (a menu item's own data source).
2689
+ */
2690
+ const OWNED_CCOLLECTION_KEY = "_bruceOwnedCollection";
2691
+ function MarkCCollectionOwned(collection) {
2692
+ if (collection) {
2693
+ collection[OWNED_CCOLLECTION_KEY] = true;
2694
+ }
2695
+ }
2696
+ function IsCCollectionOwned(collection) {
2697
+ return Boolean(collection && collection[OWNED_CCOLLECTION_KEY]);
2698
+ }
2699
+ /**
2700
+ * Runs a bulk collection mutation with every given collection's events suspended, so Cesium raises one
2701
+ * collectionChanged per collection for the whole pass instead of one per entity.
2702
+ *
2703
+ * Takes a set of collections rather than assuming `viewer.entities`, because managed visuals live in their menu
2704
+ * item's own data source: suspending the wrong collection silently does nothing.
2705
+ * @param collections Duplicates and empty entries are ignored.
2706
+ * @param run
2707
+ */
2708
+ function WithSuspendedCCollections(collections, run) {
2709
+ const unique = [];
2710
+ for (const collection of collections) {
2711
+ if (collection && unique.indexOf(collection) === -1) {
2712
+ unique.push(collection);
2700
2713
  }
2701
- Dispose() {
2702
- if (this._element && this._element.parentElement) {
2703
- this._element.parentElement.removeChild(this._element);
2714
+ }
2715
+ for (const collection of unique) {
2716
+ collection.suspendEvents();
2717
+ }
2718
+ try {
2719
+ return run();
2720
+ }
2721
+ finally {
2722
+ for (const collection of unique) {
2723
+ try {
2724
+ collection.resumeEvents();
2704
2725
  }
2705
- if (this._viewer) {
2706
- unmarkViewerWithWidget(this._viewer, this.widgetId, this);
2726
+ catch (e) {
2727
+ console.error(e);
2707
2728
  }
2708
2729
  }
2709
2730
  }
2710
- Widget.AWidget = AWidget;
2711
- function GetWidgets(viewer) {
2712
- if (viewer && viewer["_nextspaceWidgets"]) {
2713
- return Object.values(viewer["_nextspaceWidgets"]);
2714
- }
2715
- return [];
2731
+ }
2732
+ /**
2733
+ * Returns the collection a graphic belongs to, falling back to the viewer's own when it has never been added.
2734
+ * @param viewer
2735
+ * @param entity
2736
+ */
2737
+ function GetCEntityCollection(viewer, entity) {
2738
+ const owner = entity === null || entity === void 0 ? void 0 : entity.entityCollection;
2739
+ return owner ? owner : viewer === null || viewer === void 0 ? void 0 : viewer.entities;
2740
+ }
2741
+ /**
2742
+ * Returns whether a graphic is currently in the scene, looking in whichever collection owns it.
2743
+ * @param viewer
2744
+ * @param entity
2745
+ */
2746
+ function IsCEntityInScene(viewer, entity) {
2747
+ const collection = GetCEntityCollection(viewer, entity);
2748
+ return Boolean(collection && entity && collection.contains(entity));
2749
+ }
2750
+ /**
2751
+ * Removes a graphic from whichever collection owns it. Returns whether it removed anything.
2752
+ * @param viewer
2753
+ * @param entity
2754
+ */
2755
+ function RemoveCEntityFromScene(viewer, entity) {
2756
+ var _a;
2757
+ if (!entity) {
2758
+ return false;
2716
2759
  }
2717
- Widget.GetWidgets = GetWidgets;
2718
- })(exports.Widget || (exports.Widget = {}));
2719
- function markViewerWithWidget(viewer, widgetId, widget) {
2720
- if (!viewer["_nextspaceWidgets"]) {
2721
- viewer["_nextspaceWidgets"] = {};
2760
+ const collection = GetCEntityCollection(viewer, entity);
2761
+ if (collection === null || collection === void 0 ? void 0 : collection.contains(entity)) {
2762
+ return collection.remove(entity);
2722
2763
  }
2723
- viewer["_nextspaceWidgets"][widgetId] = widget;
2764
+ // A stale owner reference and the graphic ended up in the viewer's collection anyway.
2765
+ if (collection !== (viewer === null || viewer === void 0 ? void 0 : viewer.entities) && ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.entities) === null || _a === void 0 ? void 0 : _a.contains(entity))) {
2766
+ return viewer.entities.remove(entity);
2767
+ }
2768
+ return false;
2724
2769
  }
2725
- function unmarkViewerWithWidget(viewer, widgetId, widget) {
2726
- if (viewer["_nextspaceWidgets"]) {
2727
- delete viewer["_nextspaceWidgets"][widgetId];
2770
+ /**
2771
+ * Adds a graphic back into whichever collection owns it, or the viewer's if it has no owner yet.
2772
+ * @param viewer
2773
+ * @param entity
2774
+ */
2775
+ function AddCEntityToScene(viewer, entity, fallbackCollection) {
2776
+ if (!entity) {
2777
+ return false;
2778
+ }
2779
+ // An existing owner always wins, so a graphic is never migrated out from under whoever holds it.
2780
+ const owner = entity.entityCollection;
2781
+ const collection = owner ? owner : (fallbackCollection ? fallbackCollection : viewer === null || viewer === void 0 ? void 0 : viewer.entities);
2782
+ if (!collection || collection.contains(entity)) {
2783
+ return false;
2728
2784
  }
2785
+ collection.add(entity);
2786
+ return true;
2729
2787
  }
2730
-
2731
- (function (WidgetControlViewBar) {
2732
- class AControl {
2733
- get WidgetViewBar() {
2734
- return this._widget;
2735
- }
2736
- get Container() {
2737
- return this._container;
2788
+ /**
2789
+ * Returns whether toggling `show` on these graphics is safe, rather than having to add/remove them from the
2790
+ * entity collection to hide them.
2791
+ *
2792
+ * Toggling show is far cheaper than re-adding the graphic to the viewer however a Cesium bug means we can't always use it.
2793
+ * See: https://github.com/CesiumGS/cesium/issues/9370
2794
+ * @param parts The graphic and any sibling graphics, eg. from EntityUtils.GatherEntity.
2795
+ */
2796
+ function CanToggleShowSafely(parts) {
2797
+ if (!(parts === null || parts === void 0 ? void 0 : parts.length)) {
2798
+ return false;
2799
+ }
2800
+ for (const part of parts) {
2801
+ if (!(part instanceof Cesium.Entity)) {
2802
+ // Not an Entity, so not something whose `show` we can drive.
2803
+ return false;
2738
2804
  }
2739
- get Element() {
2740
- return this._element;
2805
+ if (part.polyline && isTruthyCValue(part.polyline.clampToGround)) {
2806
+ return false;
2741
2807
  }
2742
- get Disposed() {
2743
- return this._disposed;
2808
+ const materialGraphics = [part.polygon, part.corridor, part.ellipse, part.rectangle, part.wall, part.polylineVolume];
2809
+ for (const graphic of materialGraphics) {
2810
+ if (graphic && !isPlainColorMaterial(graphic.material)) {
2811
+ return false;
2812
+ }
2744
2813
  }
2745
- get Enabled() {
2746
- return this._enabled;
2814
+ }
2815
+ return true;
2816
+ }
2817
+ /**
2818
+ * Returns whether a material is a plain colour, which is what keeps a graphic out of Cesium's per-material batches.
2819
+ * Anything unrecognised counts as not plain, so callers stay on the conservative side.
2820
+ * @param material
2821
+ */
2822
+ function isPlainColorMaterial(material) {
2823
+ if (material == null) {
2824
+ // Cesium defaults an absent material to white, a plain colour.
2825
+ return true;
2826
+ }
2827
+ return material instanceof Cesium.ColorMaterialProperty || material instanceof Cesium.Color;
2828
+ }
2829
+ /**
2830
+ * Resolves a possibly-property boolean without needing a viewer clock, treating anything undeterminable as true
2831
+ * so callers stay on the conservative side.
2832
+ * @param value
2833
+ */
2834
+ function isTruthyCValue(value) {
2835
+ if (value == null) {
2836
+ return false;
2837
+ }
2838
+ if (typeof value.getValue === "function") {
2839
+ if (value instanceof Cesium.ConstantProperty) {
2840
+ return Boolean(value.valueOf());
2747
2841
  }
2748
- constructor(params) {
2749
- this._disposed = false;
2750
- this._enabled = false;
2751
- let { container, widget, element } = params;
2752
- this._widget = widget;
2753
- this._container = container;
2754
- this._element = element;
2842
+ // A non-constant property could be true at any moment, so assume it is.
2843
+ return true;
2844
+ }
2845
+ return Boolean(value);
2846
+ }
2847
+ /**
2848
+ * Returns if two resolved Cesium property values are equal, using the value's own equals() when it has one.
2849
+ * @param a
2850
+ * @param b
2851
+ */
2852
+ function AreCValuesEqual(a, b) {
2853
+ if (a === b) {
2854
+ return true;
2855
+ }
2856
+ // One side is null/undefined and the other isn't.
2857
+ if (a == null || b == null) {
2858
+ return false;
2859
+ }
2860
+ if (typeof a.equals === "function") {
2861
+ return a.equals(b) === true;
2862
+ }
2863
+ return false;
2864
+ }
2865
+ /**
2866
+ * Wraps a value in a ConstantProperty and assigns it to a graphic's property, but only when the resolved
2867
+ * value actually differs. Returns whether it assigned.
2868
+ * @param params
2869
+ */
2870
+ function SetCPropIfChanged(params) {
2871
+ const { viewer, graphic, property, value, compare } = params;
2872
+ if (!graphic) {
2873
+ return false;
2874
+ }
2875
+ const oldValue = GetCValue(viewer, graphic[property]);
2876
+ const isEqual = compare ? compare(oldValue, value) : AreCValuesEqual(oldValue, value);
2877
+ if (isEqual) {
2878
+ return false;
2879
+ }
2880
+ graphic[property] = new Cesium.ConstantProperty(value);
2881
+ return true;
2882
+ }
2883
+ function ColorToCColor(color) {
2884
+ return new Cesium.Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
2885
+ }
2886
+ /**
2887
+ * Removes duplicate points in a row and returns a new array.
2888
+ * Passed array is not mutated.
2889
+ * @param posses
2890
+ */
2891
+ function CullDuplicateCPosses(posses) {
2892
+ const newPosses = [];
2893
+ let lastPos = null;
2894
+ for (let i = 0; i < posses.length; i++) {
2895
+ const pos = posses[i];
2896
+ // Avoiding 'Cesium.Cartesian3.equals' because passed data might be just a json blob in certain cases.
2897
+ if (pos && (!lastPos || (pos.x != lastPos.x || pos.y != lastPos.y || pos.z != lastPos.z))) {
2898
+ newPosses.push(pos);
2899
+ lastPos = pos;
2755
2900
  }
2756
- Toggle() {
2901
+ }
2902
+ return newPosses;
2903
+ }
2904
+ /**
2905
+ * Returns if the given positions are equal.
2906
+ * @param a
2907
+ * @param b
2908
+ */
2909
+ function CompareCPosses(a, b) {
2910
+ // Same reference.
2911
+ if (a == b) {
2912
+ return true;
2913
+ }
2914
+ // Different lengths or one is null.
2915
+ if ((!a || !b) || (a.length != b.length)) {
2916
+ return false;
2917
+ }
2918
+ for (let i = 0; i < a.length; i++) {
2919
+ const posA = a[i];
2920
+ const posB = b[i];
2921
+ // Null detected.
2922
+ if (!posA || !posB) {
2923
+ return false;
2757
2924
  }
2758
- Dispose() {
2759
- this._disposed = true;
2925
+ // Position values don't match.
2926
+ if (!BModels.Cartes.IsEqualCartes3(posA, posB)) {
2927
+ return false;
2760
2928
  }
2761
2929
  }
2762
- WidgetControlViewBar.AControl = AControl;
2763
- })(exports.WidgetControlViewBar || (exports.WidgetControlViewBar = {}));
2764
-
2930
+ return true;
2931
+ }
2765
2932
  /**
2766
- * The fullscreen control is a simple toggle.
2767
- * There is no HTML element. However we do swap the icon based on the toggle state.
2933
+ * Returns if the given polygon hierarchies are equal.
2934
+ * @param a
2935
+ * @param b
2768
2936
  */
2769
- class WidgetControlViewBarFullscreen extends exports.WidgetControlViewBar.AControl {
2770
- constructor(params) {
2771
- super(params);
2937
+ function CompareCPolygonHierarchies(a, b) {
2938
+ // Same reference.
2939
+ if (a == b) {
2940
+ return true;
2772
2941
  }
2773
- Dispose() {
2774
- super.Dispose();
2942
+ // Different lengths or one is null.
2943
+ if ((!a || !b) || (a.positions.length != b.positions.length)) {
2944
+ return false;
2775
2945
  }
2776
- Toggle() {
2777
- if (this.Disposed) {
2778
- return;
2779
- }
2780
- super.Toggle();
2781
- // Toggle fullscreen.
2782
- this._enabled = false;
2783
- if (Cesium.Fullscreen.enabled) {
2784
- if (Cesium.Fullscreen.fullscreen) {
2785
- Cesium.Fullscreen.exitFullscreen();
2786
- }
2787
- else {
2788
- Cesium.Fullscreen.requestFullscreen(document.body);
2789
- this._enabled = true;
2790
- }
2946
+ // Positions don't match.
2947
+ if (!CompareCPosses(a.positions, b.positions)) {
2948
+ return false;
2949
+ }
2950
+ const holes = a.holes;
2951
+ const bHoles = b.holes;
2952
+ // Number of holes don't match.
2953
+ if (holes.length != bHoles.length) {
2954
+ return false;
2955
+ }
2956
+ for (let i = 0; i < holes.length; i++) {
2957
+ // Hole positions don't match.
2958
+ if (!CompareCPosses(holes[i].positions, bHoles[i].positions)) {
2959
+ return false;
2791
2960
  }
2792
2961
  }
2962
+ return true;
2793
2963
  }
2794
2964
 
2795
- class WidgetControlViewBarSearch extends exports.WidgetControlViewBar.AControl {
2796
- constructor(params) {
2797
- super(params);
2798
- this.STYLESHEET_ID = "nextspace-control-view-bar-searchbar-stylesheet";
2799
- this._generateStyles();
2800
- this._generateElement();
2801
- }
2802
- Dispose() {
2803
- super.Dispose();
2804
- if (this._element && this._element.parentElement) {
2805
- this._element.parentElement.removeChild(this._element);
2965
+ const CESIUM_INSPECTOR_KEY = "_nextspace_inspector";
2966
+ const CESIUM_TIMELINE_KEY = "_nextspace_timeline";
2967
+ const CESIUM_TIMELINE_LIVE_KEY = "_nextspace_timeline_live";
2968
+ const CESIUM_TIMELINE_LIVE_PADDING_KEY = "_nextspace_timeline_live_padding";
2969
+ const CESIUM_TIMELINE_INTERVAL_KEY = "_nextspace_timeline_interval";
2970
+ const CESIUM_MODEL_SPACE_KEY = "_nextspace_model_space";
2971
+ const DEFAULT_LIVE_PADDING_SECONDS = 30 * 60;
2972
+ const DEFAULT_BRIGHTNESS_SHIFT = 0.2;
2973
+ (function (ViewUtils) {
2974
+ function GatherLegacyMapTiles(params) {
2975
+ const viewer = params.viewer;
2976
+ const collection = viewer.imageryLayers;
2977
+ const tiles = [];
2978
+ for (let i = 0; i < collection.length; i++) {
2979
+ const layer = collection.get(i);
2980
+ if (layer._bName) {
2981
+ tiles.push({
2982
+ alpha: layer.alpha,
2983
+ brightness: layer.brightness,
2984
+ contrast: layer.contrast,
2985
+ hue: layer.hue,
2986
+ saturation: layer.saturation,
2987
+ gamma: layer.gamma,
2988
+ title: layer._bName,
2989
+ });
2990
+ }
2806
2991
  }
2807
- if (this._results && this._results.parentElement) {
2808
- this._results.parentElement.removeChild(this._results);
2992
+ return {
2993
+ imagery: tiles.reverse()
2994
+ };
2995
+ }
2996
+ ViewUtils.GatherLegacyMapTiles = GatherLegacyMapTiles;
2997
+ function GatherLegacyTerrainTile(params) {
2998
+ const viewer = params.viewer;
2999
+ const enabled = viewer.terrainProvider;
3000
+ if (enabled === null || enabled === void 0 ? void 0 : enabled._bName) {
3001
+ return {
3002
+ terrain: enabled._bName
3003
+ };
2809
3004
  }
3005
+ return {
3006
+ terrain: "flatterrain"
3007
+ };
2810
3008
  }
2811
- Toggle() {
2812
- if (this.Disposed) {
2813
- return;
3009
+ ViewUtils.GatherLegacyTerrainTile = GatherLegacyTerrainTile;
3010
+ function GatherMapTiles(params) {
3011
+ const viewer = params.viewer;
3012
+ const imagery = viewer.imageryLayers;
3013
+ const tiles = [];
3014
+ for (let i = 0; i < imagery.length; i++) {
3015
+ const provider = imagery.get(i);
3016
+ if (provider._bMeta) {
3017
+ const idCombo = provider._bMeta.accountId + provider._bMeta.tilesetId;
3018
+ if (!tiles.find(x => x.accountId + x.tilesetId === idCombo)) {
3019
+ tiles.push({
3020
+ accountId: provider._bMeta.accountId,
3021
+ tilesetId: provider._bMeta.tilesetId,
3022
+ alpha: provider.alpha,
3023
+ brightness: provider.brightness,
3024
+ contrast: provider.contrast,
3025
+ hue: provider.hue,
3026
+ saturation: provider.saturation,
3027
+ gamma: provider.gamma
3028
+ });
3029
+ }
3030
+ }
2814
3031
  }
2815
- super.Toggle();
2816
- this._enabled = !this._enabled;
2817
- if (this._enabled) {
2818
- this._display();
3032
+ return {
3033
+ imagery: tiles
3034
+ };
3035
+ }
3036
+ ViewUtils.GatherMapTiles = GatherMapTiles;
3037
+ function GatherTerrainTile(params) {
3038
+ const viewer = params.viewer;
3039
+ const provider = viewer.terrainProvider;
3040
+ if (provider === null || provider === void 0 ? void 0 : provider._bMeta) {
3041
+ return {
3042
+ terrain: {
3043
+ accountId: provider._bMeta.accountId,
3044
+ tilesetId: provider._bMeta.tilesetId,
3045
+ }
3046
+ };
2819
3047
  }
2820
- else {
2821
- this._hide();
3048
+ else if (provider instanceof Cesium.EllipsoidTerrainProvider) {
3049
+ return {
3050
+ terrain: {
3051
+ tilesetId: BModels.ProjectViewTile.EDefaultTerrain.FlatTerrain,
3052
+ accountId: null
3053
+ }
3054
+ };
2822
3055
  }
3056
+ return null;
2823
3057
  }
2824
- _generateStyles() {
2825
- if (document.getElementById(this.STYLESHEET_ID)) {
2826
- return;
2827
- }
2828
- const style = document.createElement("style");
2829
- style.id = this.STYLESHEET_ID;
2830
- style.innerHTML = `
2831
- .NextspaceSearchBar {
2832
- display: flex;
2833
- box-sizing: border-box;
2834
- height: 100%;
2835
- font-family: sans-serif;
3058
+ ViewUtils.GatherTerrainTile = GatherTerrainTile;
3059
+ function SetTerrainWireframeStatus(params) {
3060
+ if (!params.viewer[CESIUM_INSPECTOR_KEY]) {
3061
+ const InspectorClass = Cesium.CesiumInspector;
3062
+ if (InspectorClass) {
3063
+ const inspector = new InspectorClass(document.createElement("div"), params.viewer.scene);
3064
+ inspector.container.style.display = "none";
3065
+ params.viewer[CESIUM_INSPECTOR_KEY] = inspector;
3066
+ params.viewer.scene.requestRender();
3067
+ }
2836
3068
  }
2837
-
2838
- .NextspaceSearchBar * {
2839
- box-sizing: border-box;
3069
+ if (params.viewer[CESIUM_INSPECTOR_KEY]) {
3070
+ params.viewer[CESIUM_INSPECTOR_KEY].viewModel.wireframe = params.status;
2840
3071
  }
2841
-
2842
- .NextspaceSearchBar[is-opened="true"] .NextspaceSearchBarContent {
2843
- display: flex;
2844
- width: 260px;
3072
+ }
3073
+ ViewUtils.SetTerrainWireframeStatus = SetTerrainWireframeStatus;
3074
+ function GetTerrainWireframeStatus(params) {
3075
+ var _a, _b;
3076
+ if (!params.viewer[CESIUM_INSPECTOR_KEY]) {
3077
+ return false;
2845
3078
  }
2846
-
2847
- .NextspaceSearchBarContent {
2848
- flex-grow: 0;
2849
- flex-shrink: 0;
3079
+ return (_b = (_a = params.viewer[CESIUM_INSPECTOR_KEY]) === null || _a === void 0 ? void 0 : _a.viewModel) === null || _b === void 0 ? void 0 : _b.wireframe;
3080
+ }
3081
+ ViewUtils.GetTerrainWireframeStatus = GetTerrainWireframeStatus;
3082
+ /**
3083
+ * Changes between perspective and orthographic view.
3084
+ * When Cesium stops being bad at picking positions in 2d mode we'll use the flat earth mode instead.
3085
+ * @param params
3086
+ */
3087
+ function Set2dStatus(params) {
3088
+ const { viewer, status: is2d, moveCamera } = params;
3089
+ const curLens2d = viewer.camera.frustum instanceof Cesium.OrthographicFrustum;
3090
+ if (curLens2d && !is2d) {
3091
+ OnNextCRender(viewer, () => {
3092
+ viewer.camera.switchToPerspectiveFrustum();
3093
+ viewer.scene.screenSpaceCameraController.enableTilt = true;
3094
+ });
3095
+ viewer.scene.requestRender();
3096
+ }
3097
+ else if (!curLens2d && is2d) {
3098
+ OnNextCRender(viewer, () => {
3099
+ viewer.camera.switchToOrthographicFrustum();
3100
+ viewer.scene.screenSpaceCameraController.enableTilt = false;
3101
+ });
3102
+ if (moveCamera != false) {
3103
+ try {
3104
+ // Face camera downwards to make it look 2d.
3105
+ // We want to try make it look at the center-point of the current view.
3106
+ // If center cannot be calculated then we'll simply raise the camera and face it downwards.
3107
+ const scene = viewer.scene;
3108
+ const windowPosition = new Cesium.Cartesian2(scene.canvas.clientWidth / 2, scene.canvas.clientHeight / 2);
3109
+ const ray = viewer.camera.getPickRay(windowPosition);
3110
+ const intersection = scene.globe.pick(ray, scene);
3111
+ let center;
3112
+ if (Cesium.defined(intersection)) {
3113
+ center = Cesium.Cartographic.fromCartesian(intersection);
3114
+ }
3115
+ // Use current camera position if we can't calculate the center.
3116
+ else {
3117
+ center = Cesium.Cartographic.fromCartesian(viewer.camera.position);
3118
+ center.height = 0;
3119
+ }
3120
+ center.height = viewer.camera.positionCartographic.height + 100;
3121
+ viewer.camera.setView({
3122
+ destination: Cesium.Cartographic.toCartesian(center),
3123
+ orientation: {
3124
+ heading: 0.0,
3125
+ pitch: Cesium.Math.toRadians(-90.0),
3126
+ roll: 0.0
3127
+ }
3128
+ });
3129
+ }
3130
+ catch (e) {
3131
+ console.error(e);
3132
+ }
3133
+ }
3134
+ viewer.scene.requestRender();
3135
+ }
3136
+ }
3137
+ ViewUtils.Set2dStatus = Set2dStatus;
3138
+ function Get2dStatus(params) {
3139
+ const { viewer } = params;
3140
+ return viewer.camera.frustum instanceof Cesium.OrthographicFrustum;
3141
+ }
3142
+ ViewUtils.Get2dStatus = Get2dStatus;
3143
+ function SetLockedCameraStatus(params) {
3144
+ const { viewer, status } = params;
3145
+ const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
3146
+ if (!scene) {
3147
+ return;
3148
+ }
3149
+ if (status) {
3150
+ scene.screenSpaceCameraController.enableInputs = false;
3151
+ scene.screenSpaceCameraController.enableTranslate = false;
3152
+ scene.screenSpaceCameraController.enableZoom = false;
3153
+ scene.screenSpaceCameraController.enableRotate = false;
3154
+ scene.screenSpaceCameraController.enableTilt = false;
3155
+ }
3156
+ else {
3157
+ scene.screenSpaceCameraController.enableInputs = true;
3158
+ scene.screenSpaceCameraController.enableTranslate = true;
3159
+ scene.screenSpaceCameraController.enableZoom = true;
3160
+ scene.screenSpaceCameraController.enableRotate = true;
3161
+ if (!ViewUtils.Get2dStatus({ viewer })) {
3162
+ scene.screenSpaceCameraController.enableTilt = true;
3163
+ }
3164
+ }
3165
+ viewer.scene.requestRender();
3166
+ }
3167
+ ViewUtils.SetLockedCameraStatus = SetLockedCameraStatus;
3168
+ function GetLockedCameraStatus(params) {
3169
+ var _a;
3170
+ const scene = (_a = params.viewer) === null || _a === void 0 ? void 0 : _a.scene;
3171
+ if (!scene) {
3172
+ return false;
3173
+ }
3174
+ return !scene.screenSpaceCameraController.enableTranslate;
3175
+ }
3176
+ ViewUtils.GetLockedCameraStatus = GetLockedCameraStatus;
3177
+ /**
3178
+ * Returns the current camera time and related settings.
3179
+ * @param params
3180
+ * @returns
3181
+ */
3182
+ function GetTimeDetails(params) {
3183
+ const { viewer } = params;
3184
+ const clock = viewer.clock;
3185
+ return {
3186
+ animationSpeed: clock.multiplier,
3187
+ isAnimating: clock.shouldAnimate,
3188
+ startTime: clock.startTime,
3189
+ startTimeIso: clock.startTime.toString(),
3190
+ currentTime: clock.currentTime,
3191
+ currentTimeIso: clock.currentTime.toString(),
3192
+ stopTime: clock.stopTime,
3193
+ stopTimeIso: clock.stopTime.toString(),
3194
+ areCesiumValues: clock[CESIUM_TIMELINE_KEY] != true,
3195
+ isLive: clock[CESIUM_TIMELINE_LIVE_KEY] == true,
3196
+ livePaddingSeconds: clock[CESIUM_TIMELINE_LIVE_PADDING_KEY] || DEFAULT_LIVE_PADDING_SECONDS
3197
+ };
3198
+ }
3199
+ ViewUtils.GetTimeDetails = GetTimeDetails;
3200
+ /**
3201
+ * Sets the Cesium clock values.
3202
+ * If a value is not provided, then 'areCesiumValues' checked to see if the values are Cesium values.
3203
+ * If they're currently Cesium set values then our defaults are used.
3204
+ * @param params
3205
+ */
3206
+ function SetTimeDetails(params) {
3207
+ let { viewer, animationSpeed, isAnimating, startTime, currentTime, stopTime, startTimeIso, stopTimeIso, currentTimeIso, isLive, livePaddingSeconds } = params;
3208
+ const clock = viewer.clock;
3209
+ if (startTime) {
3210
+ clock.startTime = startTime;
3211
+ }
3212
+ else if (startTimeIso) {
3213
+ const startDate = Cesium.JulianDate.fromIso8601(startTimeIso);
3214
+ if (startDate) {
3215
+ clock.startTime = startDate;
3216
+ }
3217
+ }
3218
+ if (currentTime) {
3219
+ clock.currentTime = currentTime;
3220
+ }
3221
+ else if (currentTimeIso) {
3222
+ const currentDate = Cesium.JulianDate.fromIso8601(currentTimeIso);
3223
+ if (currentDate) {
3224
+ clock.currentTime = currentDate;
3225
+ }
3226
+ }
3227
+ if (stopTime) {
3228
+ clock.stopTime = stopTime;
3229
+ }
3230
+ else if (stopTimeIso) {
3231
+ const stopDate = Cesium.JulianDate.fromIso8601(stopTimeIso);
3232
+ if (stopDate) {
3233
+ clock.stopTime = stopDate;
3234
+ }
3235
+ }
3236
+ clock.clockRange = Cesium.ClockRange.LOOP_STOP;
3237
+ const areCesiumValues = clock[CESIUM_TIMELINE_KEY] != true;
3238
+ if (areCesiumValues) {
3239
+ if (isNaN(animationSpeed) || animationSpeed == null) {
3240
+ animationSpeed = 1;
3241
+ }
3242
+ if (isAnimating == null) {
3243
+ isAnimating = false;
3244
+ }
3245
+ if (isLive == null) {
3246
+ isLive = false;
3247
+ }
3248
+ if (livePaddingSeconds == null) {
3249
+ livePaddingSeconds = DEFAULT_LIVE_PADDING_SECONDS;
3250
+ }
3251
+ }
3252
+ if (!isNaN(animationSpeed) && animationSpeed != null) {
3253
+ clock.multiplier = animationSpeed;
3254
+ }
3255
+ if (isAnimating != null) {
3256
+ clock.shouldAnimate = isAnimating;
3257
+ }
3258
+ viewer.scene.requestRender();
3259
+ clock[CESIUM_TIMELINE_KEY] = true;
3260
+ if (livePaddingSeconds != null) {
3261
+ clock[CESIUM_TIMELINE_LIVE_PADDING_KEY] = livePaddingSeconds || DEFAULT_LIVE_PADDING_SECONDS;
3262
+ }
3263
+ if (isLive != null) {
3264
+ clock[CESIUM_TIMELINE_LIVE_KEY] = params.isLive == true;
3265
+ }
3266
+ if (clock[CESIUM_TIMELINE_LIVE_KEY]) {
3267
+ startLive(viewer);
3268
+ }
3269
+ else {
3270
+ stopLive(viewer);
3271
+ }
3272
+ }
3273
+ ViewUtils.SetTimeDetails = SetTimeDetails;
3274
+ function startLive(viewer) {
3275
+ const doUpdate = () => {
3276
+ if (!viewer || viewer.isDestroyed() || !viewer.scene) {
3277
+ stopLive(viewer);
3278
+ return;
3279
+ }
3280
+ // Get the current settings.
3281
+ const tDetails = GetTimeDetails({ viewer });
3282
+ const clock = viewer.clock;
3283
+ if (clock) {
3284
+ clock.shouldAnimate = true;
3285
+ clock.multiplier = 1;
3286
+ clock.currentTime = Cesium.JulianDate.now();
3287
+ clock.startTime = Cesium.JulianDate.addSeconds(clock.currentTime, -tDetails.livePaddingSeconds, new Cesium.JulianDate());
3288
+ clock.stopTime = Cesium.JulianDate.addSeconds(clock.currentTime, tDetails.livePaddingSeconds, new Cesium.JulianDate());
3289
+ clock.clockRange = Cesium.ClockRange.UNBOUNDED;
3290
+ }
3291
+ };
3292
+ if (viewer[CESIUM_TIMELINE_INTERVAL_KEY]) {
3293
+ doUpdate();
3294
+ return;
3295
+ }
3296
+ // We'll start an interval that occasionally updates the Cesium clock to stay in the desired settings.
3297
+ // Since it moves live (1 second per second), we don't have to worry about the ranges being out of date quickly.
3298
+ viewer[CESIUM_TIMELINE_INTERVAL_KEY] = setInterval(() => {
3299
+ doUpdate();
3300
+ }, 800);
3301
+ viewer[CESIUM_TIMELINE_LIVE_KEY] = true;
3302
+ // Initial update.
3303
+ doUpdate();
3304
+ }
3305
+ function stopLive(viewer) {
3306
+ if (!viewer) {
3307
+ return;
3308
+ }
3309
+ let interval = viewer[CESIUM_TIMELINE_INTERVAL_KEY];
3310
+ if (interval) {
3311
+ clearInterval(interval);
3312
+ viewer[CESIUM_TIMELINE_INTERVAL_KEY] = null;
3313
+ }
3314
+ viewer[CESIUM_TIMELINE_LIVE_KEY] = false;
3315
+ }
3316
+ /**
3317
+ * Brightens a scene's atmosphere, using whichever control the host app's CesiumJS exposes.
3318
+ * @param viewer the viewer to brighten. Safe to call with null.
3319
+ * @param shift -1 to 1, where 0 or less leaves CesiumJS's own defaults alone.
3320
+ */
3321
+ function ApplyDefaultBrightness(viewer, shift = DEFAULT_BRIGHTNESS_SHIFT) {
3322
+ const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
3323
+ if (!scene || !(shift > 0)) {
3324
+ return;
3325
+ }
3326
+ // Atmosphere settings moved from Globe onto Scene, and the sky keeps its own copy, so each is
3327
+ // probed rather than version checked: host apps bring their own CesiumJS.
3328
+ const SCENE = scene;
3329
+ if (SCENE.atmosphere && typeof SCENE.atmosphere.brightnessShift === "number") {
3330
+ SCENE.atmosphere.brightnessShift = shift;
3331
+ }
3332
+ if (SCENE.globe && typeof SCENE.globe.atmosphereBrightnessShift === "number") {
3333
+ SCENE.globe.atmosphereBrightnessShift = shift;
3334
+ }
3335
+ if (SCENE.skyAtmosphere && typeof SCENE.skyAtmosphere.brightnessShift === "number") {
3336
+ SCENE.skyAtmosphere.brightnessShift = shift;
3337
+ }
3338
+ }
3339
+ ViewUtils.ApplyDefaultBrightness = ApplyDefaultBrightness;
3340
+ function SetGlobeDetails(params) {
3341
+ let { viewer, hidden, baseColor } = params;
3342
+ const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
3343
+ if (!(scene === null || scene === void 0 ? void 0 : scene.globe)) {
3344
+ return;
3345
+ }
3346
+ if (hidden == null) {
3347
+ hidden = !scene.globe.show;
3348
+ }
3349
+ else {
3350
+ scene.globe.show = !hidden;
3351
+ }
3352
+ if (baseColor == null) {
3353
+ baseColor = scene.globe.baseColor;
3354
+ }
3355
+ // When globe is off, we'll also hide the stars/moon.
3356
+ // We also grab the globe color and apply it to the sky.
3357
+ if (hidden && GetModelSpace(viewer)) {
3358
+ scene.skyBox.show = false;
3359
+ scene.skyAtmosphere.show = false;
3360
+ scene.sun.show = false;
3361
+ scene.moon.show = false;
3362
+ scene.backgroundColor = baseColor.clone();
3363
+ }
3364
+ else {
3365
+ scene.skyBox.show = true;
3366
+ scene.skyAtmosphere.show = true;
3367
+ scene.sun.show = true;
3368
+ scene.moon.show = true;
3369
+ scene.backgroundColor = Cesium.Color.BLACK;
3370
+ }
3371
+ scene.globe.baseColor = baseColor;
3372
+ }
3373
+ ViewUtils.SetGlobeDetails = SetGlobeDetails;
3374
+ function SetModelSpace(viewer, modelSpace) {
3375
+ if (!viewer) {
3376
+ return;
3377
+ }
3378
+ if (viewer[CESIUM_MODEL_SPACE_KEY] === modelSpace) {
3379
+ return;
3380
+ }
3381
+ viewer[CESIUM_MODEL_SPACE_KEY] = modelSpace;
3382
+ // Reload globe since we display it differently between the two modes.
3383
+ SetGlobeDetails({ viewer });
3384
+ }
3385
+ ViewUtils.SetModelSpace = SetModelSpace;
3386
+ function GetModelSpace(viewer) {
3387
+ if (!viewer) {
3388
+ return false;
3389
+ }
3390
+ return Boolean(viewer[CESIUM_MODEL_SPACE_KEY]);
3391
+ }
3392
+ ViewUtils.GetModelSpace = GetModelSpace;
3393
+ })(exports.ViewUtils || (exports.ViewUtils = {}));
3394
+
3395
+ (function (Widget) {
3396
+ class AWidget {
3397
+ get WidgetId() {
3398
+ return this.widgetId;
3399
+ }
3400
+ get Viewer() {
3401
+ return this._viewer;
3402
+ }
3403
+ get Container() {
3404
+ return this._container;
3405
+ }
3406
+ get Element() {
3407
+ return this._element;
3408
+ }
3409
+ constructor(params) {
3410
+ this.widgetId = BModels.ObjectUtils.UId();
3411
+ let { viewer, container, manager } = params;
3412
+ // A host app can pass an undefined viewer (eg. mid-teardown); fall back to
3413
+ // no container rather than crashing on viewer.container.
3414
+ if (!container && viewer) {
3415
+ container = viewer.container;
3416
+ }
3417
+ this._viewer = viewer;
3418
+ this._manager = manager;
3419
+ this._container = container;
3420
+ this._apiGetters = params.apiGetters;
3421
+ if (viewer) {
3422
+ markViewerWithWidget(viewer, this.widgetId, this);
3423
+ }
3424
+ }
3425
+ Dispose() {
3426
+ if (this._element && this._element.parentElement) {
3427
+ this._element.parentElement.removeChild(this._element);
3428
+ }
3429
+ if (this._viewer) {
3430
+ unmarkViewerWithWidget(this._viewer, this.widgetId, this);
3431
+ }
3432
+ }
3433
+ }
3434
+ Widget.AWidget = AWidget;
3435
+ function GetWidgets(viewer) {
3436
+ if (viewer && viewer["_nextspaceWidgets"]) {
3437
+ return Object.values(viewer["_nextspaceWidgets"]);
3438
+ }
3439
+ return [];
3440
+ }
3441
+ Widget.GetWidgets = GetWidgets;
3442
+ })(exports.Widget || (exports.Widget = {}));
3443
+ function markViewerWithWidget(viewer, widgetId, widget) {
3444
+ if (!viewer["_nextspaceWidgets"]) {
3445
+ viewer["_nextspaceWidgets"] = {};
3446
+ }
3447
+ viewer["_nextspaceWidgets"][widgetId] = widget;
3448
+ }
3449
+ function unmarkViewerWithWidget(viewer, widgetId, widget) {
3450
+ if (viewer["_nextspaceWidgets"]) {
3451
+ delete viewer["_nextspaceWidgets"][widgetId];
3452
+ }
3453
+ }
3454
+
3455
+ (function (WidgetControlViewBar) {
3456
+ class AControl {
3457
+ get WidgetViewBar() {
3458
+ return this._widget;
3459
+ }
3460
+ get Container() {
3461
+ return this._container;
3462
+ }
3463
+ get Element() {
3464
+ return this._element;
3465
+ }
3466
+ get Disposed() {
3467
+ return this._disposed;
3468
+ }
3469
+ get Enabled() {
3470
+ return this._enabled;
3471
+ }
3472
+ constructor(params) {
3473
+ this._disposed = false;
3474
+ this._enabled = false;
3475
+ let { container, widget, element } = params;
3476
+ this._widget = widget;
3477
+ this._container = container;
3478
+ this._element = element;
3479
+ }
3480
+ Toggle() {
3481
+ }
3482
+ Dispose() {
3483
+ this._disposed = true;
3484
+ }
3485
+ }
3486
+ WidgetControlViewBar.AControl = AControl;
3487
+ })(exports.WidgetControlViewBar || (exports.WidgetControlViewBar = {}));
3488
+
3489
+ /**
3490
+ * The fullscreen control is a simple toggle.
3491
+ * There is no HTML element. However we do swap the icon based on the toggle state.
3492
+ */
3493
+ class WidgetControlViewBarFullscreen extends exports.WidgetControlViewBar.AControl {
3494
+ constructor(params) {
3495
+ super(params);
3496
+ }
3497
+ Dispose() {
3498
+ super.Dispose();
3499
+ }
3500
+ Toggle() {
3501
+ if (this.Disposed) {
3502
+ return;
3503
+ }
3504
+ super.Toggle();
3505
+ // Toggle fullscreen.
3506
+ this._enabled = false;
3507
+ if (Cesium.Fullscreen.enabled) {
3508
+ if (Cesium.Fullscreen.fullscreen) {
3509
+ Cesium.Fullscreen.exitFullscreen();
3510
+ }
3511
+ else {
3512
+ Cesium.Fullscreen.requestFullscreen(document.body);
3513
+ this._enabled = true;
3514
+ }
3515
+ }
3516
+ }
3517
+ }
3518
+
3519
+ class WidgetControlViewBarSearch extends exports.WidgetControlViewBar.AControl {
3520
+ constructor(params) {
3521
+ super(params);
3522
+ this.STYLESHEET_ID = "nextspace-control-view-bar-searchbar-stylesheet";
3523
+ this._generateStyles();
3524
+ this._generateElement();
3525
+ }
3526
+ Dispose() {
3527
+ super.Dispose();
3528
+ if (this._element && this._element.parentElement) {
3529
+ this._element.parentElement.removeChild(this._element);
3530
+ }
3531
+ if (this._results && this._results.parentElement) {
3532
+ this._results.parentElement.removeChild(this._results);
3533
+ }
3534
+ }
3535
+ Toggle() {
3536
+ if (this.Disposed) {
3537
+ return;
3538
+ }
3539
+ super.Toggle();
3540
+ this._enabled = !this._enabled;
3541
+ if (this._enabled) {
3542
+ this._display();
3543
+ }
3544
+ else {
3545
+ this._hide();
3546
+ }
3547
+ }
3548
+ _generateStyles() {
3549
+ if (document.getElementById(this.STYLESHEET_ID)) {
3550
+ return;
3551
+ }
3552
+ const style = document.createElement("style");
3553
+ style.id = this.STYLESHEET_ID;
3554
+ style.innerHTML = `
3555
+ .NextspaceSearchBar {
3556
+ display: flex;
3557
+ box-sizing: border-box;
3558
+ height: 100%;
3559
+ font-family: sans-serif;
3560
+ }
3561
+
3562
+ .NextspaceSearchBar * {
3563
+ box-sizing: border-box;
3564
+ }
3565
+
3566
+ .NextspaceSearchBar[is-opened="true"] .NextspaceSearchBarContent {
3567
+ display: flex;
3568
+ width: 260px;
3569
+ }
3570
+
3571
+ .NextspaceSearchBarContent {
3572
+ flex-grow: 0;
3573
+ flex-shrink: 0;
2850
3574
  overflow: hidden;
2851
3575
  transition: .3s ease;
2852
3576
  width: 0;
@@ -3553,1122 +4277,423 @@
3553
4277
  for (let i = 0; i < geometry.Polygon.length; i++) {
3554
4278
  const polygon = geometry.Polygon[i];
3555
4279
  count += BModels.Geometry.ParsePoints(polygon.LinearRing).length;
3556
- }
3557
- }
3558
- else if (geometry.LineString) {
3559
- count += BModels.Geometry.ParsePoints(geometry.LineString).length;
3560
- }
3561
- else if (geometry.Point) {
3562
- count += 1;
3563
- }
3564
- };
3565
- traverse(geometry);
3566
- return count;
3567
- }
3568
- SimplifyGeometry.CountPoints = CountPoints;
3569
- /**
3570
- * Simplifies input geometry.
3571
- * This will turn it into GeoJSON, run it through turf, then back to Bruce geometry.
3572
- * @param geometry
3573
- */
3574
- function Simplify(entityId, geometry, tolerance) {
3575
- var _a;
3576
- if (!geometry || !isTurfAvailable() || !turf.simplify) {
3577
- return geometry;
3578
- }
3579
- const original = geometry;
3580
- // Convert to geojson so that we can interact with turf.
3581
- const gFeature = BModels.Geometry.ToGeoJsonFeature({
3582
- geometry: geometry
3583
- });
3584
- // Unkink the geometry.
3585
- // This removes overlapping polygons before we start merging and simplifying.
3586
- // Commented out because it has crashes and doesn't handle holes.
3587
- untangleFeature(gFeature);
3588
- // Union the geometry.
3589
- // This merges overlapping polygons.
3590
- unionFeature(gFeature);
3591
- // Simplify the geometry.
3592
- if (tolerance > 0) {
3593
- gFeature.geometry = turf.simplify(gFeature.geometry, {
3594
- tolerance: tolerance,
3595
- highQuality: true,
3596
- mutate: true
3597
- });
3598
- }
3599
- // Converting back to Bruce geometry.
3600
- const simplified = (_a = BModels.Geometry.FromGeoJson({
3601
- geoJson: gFeature
3602
- })) === null || _a === void 0 ? void 0 : _a.geometry;
3603
- // A polygon we cannot vouch for is worse than an unsimplified one, so fall back instead of shipping it.
3604
- if (!simplified || !isSafeToRender(simplified)) {
3605
- return original;
3606
- }
3607
- return simplified;
3608
- }
3609
- SimplifyGeometry.Simplify = Simplify;
3610
- /**
3611
- * Whether a simplified geometry still describes something a renderer can triangulate.
3612
- * @param geometry the simplification result.
3613
- * @returns false when any polygon has a collapsed ring, a hole outside its outer ring, or a hole
3614
- * wound the same way as its outer ring.
3615
- */
3616
- function IsSafeToRender(geometry) {
3617
- return isSafeToRender(geometry);
3618
- }
3619
- SimplifyGeometry.IsSafeToRender = IsSafeToRender;
3620
- })(SimplifyGeometry || (SimplifyGeometry = {}));
3621
- // A ring needs three distinct corners plus the closing repeat before it encloses any area.
3622
- const MIN_RING_POSITIONS = 4;
3623
- function isSafeToRender(geometry) {
3624
- let safe = true;
3625
- const check = (g) => {
3626
- if (!safe) {
3627
- return;
3628
- }
3629
- if (g === null || g === void 0 ? void 0 : g.MultiGeometry) {
3630
- g.MultiGeometry.forEach(check);
3631
- return;
3632
- }
3633
- if (!(g === null || g === void 0 ? void 0 : g.Polygon) || g.Polygon.length === 0) {
3634
- return;
3635
- }
3636
- let outer = null;
3637
- let outerIsClockwise = false;
3638
- for (let i = 0; i < g.Polygon.length; i++) {
3639
- const points = BModels.Geometry.ParsePoints(g.Polygon[i].LinearRing);
3640
- if (!points || points.length < MIN_RING_POSITIONS) {
3641
- safe = false;
3642
- return;
3643
- }
3644
- const clockwise = signedRingArea(points) < 0;
3645
- if (g.Polygon[i].Facing !== "in") {
3646
- outer = points;
3647
- outerIsClockwise = clockwise;
3648
- continue;
3649
- }
3650
- // A hole with no outer ring ahead of it cannot be subtracted from anything.
3651
- if (!outer) {
3652
- safe = false;
3653
- return;
3654
- }
3655
- if (clockwise === outerIsClockwise) {
3656
- safe = false;
3657
- return;
3658
- }
3659
- for (let j = 0; j < points.length; j++) {
3660
- if (!isPointInRing(points[j], outer)) {
3661
- safe = false;
3662
- return;
3663
- }
3664
- }
3665
- }
3666
- };
3667
- check(geometry);
3668
- return safe;
3669
- }
3670
- function signedRingArea(points) {
3671
- let sum = 0;
3672
- for (let i = 0; i < points.length; i++) {
3673
- const a = points[i];
3674
- const b = points[(i + 1) % points.length];
3675
- sum += a.longitude * b.latitude - b.longitude * a.latitude;
3676
- }
3677
- return sum / 2;
3678
- }
3679
- function isPointInRing(point, ring) {
3680
- let inside = false;
3681
- for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
3682
- const a = ring[i];
3683
- const b = ring[j];
3684
- if ((a.latitude > point.latitude) !== (b.latitude > point.latitude)
3685
- && point.longitude < (b.longitude - a.longitude) * (point.latitude - a.latitude) / (b.latitude - a.latitude) + a.longitude) {
3686
- inside = !inside;
3687
- }
3688
- }
3689
- return inside;
3690
- }
3691
- /**
3692
- * Runs through all found polygons and runs unkink through turf on them.
3693
- * @param feature
3694
- */
3695
- function untangleFeature(feature) {
3696
- // Collection to add to because the result might be multiple from an unkink.
3697
- const collection = {
3698
- geometries: [],
3699
- type: "GeometryCollection"
3700
- };
3701
- // Runs a dedupe and unkink on the coordinates.
3702
- // Returns if the coordinates were added to the collection.
3703
- const processCoordinates = (coords) => {
3704
- try {
3705
- // Dedupe the coordinates to avoid issues with unkink.
3706
- coords = dedupeCoordinates(coords);
3707
- const outer = [];
3708
- let inner = [];
3709
- // We'll unkink each ring separately as this is killing the holes when it is done as a whole.
3710
- // So we'll do it separate then recombine.
3711
- for (let i = 0; i < coords.length; i++) {
3712
- const ring = coords[i];
3713
- const unkink = turf.unkinkPolygon({
3714
- type: "Polygon",
3715
- coordinates: [ring]
3716
- });
3717
- if (unkink.type == "FeatureCollection") {
3718
- let target = i == 0 ? outer : inner;
3719
- for (let j = 0; j < unkink.features.length; j++) {
3720
- const unkinked = unkink.features[j].geometry.coordinates;
3721
- if (unkinked.length > 0) {
3722
- target.push(unkinked[0]);
3723
- }
3724
- }
3725
- }
3726
- else {
3727
- console.error("Unexpected unkink result.", unkink);
3728
- }
3729
- }
3730
- // Recreate the rings and reapply to the collection.
3731
- if (outer.length > 0) {
3732
- // Unkinking a self touching boundary splits it into pieces. Keeping whichever piece
3733
- // happened to come back first can discard most of the shape, so keep the biggest one.
3734
- let keep = outer[0];
3735
- if (outer.length > 1) {
3736
- let best = Math.abs(ringArea(keep));
3737
- for (let i = 1; i < outer.length; i++) {
3738
- const area = Math.abs(ringArea(outer[i]));
3739
- if (area > best) {
3740
- best = area;
3741
- keep = outer[i];
3742
- }
3743
- }
3744
- }
3745
- const combinedCoords = [keep];
3746
- for (let i = 0; i < inner.length; i++) {
3747
- // A hole belonging to a piece that was just discarded is no longer inside anything,
3748
- // and a hole crossing the boundary is what tears the fill.
3749
- if (isRingInsideRing(inner[i], keep)) {
3750
- combinedCoords.push(inner[i]);
3751
- }
3752
- }
3753
- // Add the combined coordinates to the collection
3754
- const polygon = {
3755
- type: "Polygon",
3756
- coordinates: combinedCoords
3757
- };
3758
- // Ensure right-hand rule is followed.
3759
- ensureRightHandRule(polygon);
3760
- // Add to the collection.
3761
- collection.geometries.push(polygon);
3762
- return true;
3763
- }
3764
- }
3765
- catch (e) {
3766
- // console.error("Failed to unkink polygon.", e);
3767
- }
3768
- return false;
3769
- };
3770
- const processGeometry = (geometry) => {
3771
- let added = false;
3772
- if (geometry.type == "Polygon") {
3773
- added = processCoordinates(geometry.coordinates);
3774
- }
3775
- else if (geometry.type == "MultiPolygon") {
3776
- for (let i = 0; i < geometry.coordinates.length; i++) {
3777
- added = processCoordinates(geometry.coordinates[i]) || added;
3778
- }
3779
- }
3780
- if (!added) {
3781
- // Adding original since we can't unkink it.
3782
- collection.geometries.push(geometry);
3783
- }
3784
- };
3785
- if (feature.geometry.type == "GeometryCollection") {
3786
- feature.geometry.geometries.forEach((geometry) => {
3787
- processGeometry(geometry);
3788
- });
3789
- }
3790
- else {
3791
- processGeometry(feature.geometry);
3792
- }
3793
- // Re-assign the geometry.
3794
- if (collection.geometries.length == 1) {
3795
- feature.geometry = collection.geometries[0];
3796
- }
3797
- else {
3798
- feature.geometry = collection;
3799
- }
3800
- }
3801
- /**
3802
- * Runs through all found polygons and unions them using turf.
3803
- * Failed unions are ignored and the original is kept.
3804
- * @param feature
3805
- */
3806
- function unionFeature(feature) {
3807
- // Collection to add to because the result might be multiple from an unkink.
3808
- const collection = {
3809
- geometries: [],
3810
- type: "GeometryCollection"
3811
- };
3812
- // We'll turn each geometry into a feature and union them.
3813
- const tmpCollection = {
3814
- features: [],
3815
- type: "FeatureCollection"
3816
- };
3817
- if (feature.geometry.type == "GeometryCollection") {
3818
- feature.geometry.geometries.forEach((geometry) => {
3819
- if (geometry.type == "Polygon") {
3820
- tmpCollection.features.push({
3821
- type: "Feature",
3822
- geometry: geometry,
3823
- properties: {}
3824
- });
3825
- }
3826
- else if (geometry.type == "MultiPolygon") {
3827
- for (let i = 0; i < geometry.coordinates.length; i++) {
3828
- tmpCollection.features.push({
3829
- type: "Feature",
3830
- geometry: {
3831
- type: "Polygon",
3832
- coordinates: geometry.coordinates[i]
3833
- },
3834
- properties: {}
3835
- });
3836
- }
3837
- }
3838
- // Only focusing on polygons.
3839
- else {
3840
- collection.geometries.push(geometry);
3841
- }
3842
- });
3843
- }
3844
- else if (feature.geometry.type == "Polygon") {
3845
- tmpCollection.features.push({
3846
- type: "Feature",
3847
- geometry: feature.geometry,
3848
- properties: {}
3849
- });
3850
- }
3851
- else if (feature.geometry.type == "MultiPolygon") {
3852
- for (let i = 0; i < feature.geometry.coordinates.length; i++) {
3853
- tmpCollection.features.push({
3854
- type: "Feature",
3855
- geometry: {
3856
- type: "Polygon",
3857
- coordinates: feature.geometry.coordinates[i]
3858
- },
3859
- properties: {}
3860
- });
3861
- }
3862
- }
3863
- // No polygons to union.
3864
- // Need at least 2 to union.
3865
- if (tmpCollection.features.length < 2) {
3866
- return;
3867
- }
3868
- // Now we'll union them.
3869
- try {
3870
- const union = turf.union(tmpCollection);
3871
- if (union.geometry.type == "Polygon") {
3872
- collection.geometries.push(union.geometry);
3873
- }
3874
- else if (union.geometry.type == "MultiPolygon") {
3875
- for (let i = 0; i < union.geometry.coordinates.length; i++) {
3876
- collection.geometries.push({
3877
- type: "Polygon",
3878
- coordinates: union.geometry.coordinates[i]
3879
- });
3880
- }
3881
- }
3882
- else {
3883
- // Returning early because the result is unexpected.
3884
- return;
3885
- }
3886
- }
3887
- catch (e) {
3888
- // console.error("Failed to union polygons.", e);
3889
- return;
3890
- }
3891
- // Re-assign the geometry.
3892
- if (collection.geometries.length == 1) {
3893
- feature.geometry = collection.geometries[0];
3894
- }
3895
- else {
3896
- feature.geometry = collection;
3897
- }
3898
- }
3899
- function ringArea(ring) {
3900
- let sum = 0;
3901
- for (let i = 0; i < ring.length; i++) {
3902
- const a = ring[i];
3903
- const b = ring[(i + 1) % ring.length];
3904
- sum += a[0] * b[1] - b[0] * a[1];
3905
- }
3906
- return sum / 2;
3907
- }
3908
- function isRingInsideRing(inner, outer) {
3909
- for (let i = 0; i < inner.length; i++) {
3910
- let inside = false;
3911
- for (let a = 0, b = outer.length - 1; a < outer.length; b = a++) {
3912
- if ((outer[a][1] > inner[i][1]) !== (outer[b][1] > inner[i][1])
3913
- && inner[i][0] < (outer[b][0] - outer[a][0]) * (inner[i][1] - outer[a][1]) / (outer[b][1] - outer[a][1]) + outer[a][0]) {
3914
- inside = !inside;
3915
- }
3916
- }
3917
- if (!inside) {
3918
- return false;
3919
- }
3920
- }
3921
- return true;
3922
- }
3923
- function dedupeCoordinates(inputCoords) {
3924
- return inputCoords.map(ring => {
3925
- if (ring.length < 2) {
3926
- return ring;
3927
- }
3928
- const dedupeCoords = [];
3929
- const uniqueCoords = new Set();
3930
- // Dedupe the points.
3931
- ring.forEach(coord => {
3932
- const key = coord.join(',');
3933
- if (!uniqueCoords.has(key)) {
3934
- uniqueCoords.add(key);
3935
- dedupeCoords.push(coord);
3936
- }
3937
- });
3938
- // Make sure the last point matches the first.
3939
- if (dedupeCoords.length > 1) {
3940
- if (dedupeCoords[0][0] != dedupeCoords[dedupeCoords.length - 1][0] ||
3941
- dedupeCoords[0][1] != dedupeCoords[dedupeCoords.length - 1][1]) {
3942
- dedupeCoords.push(dedupeCoords[0]);
3943
- }
3944
- }
3945
- return dedupeCoords;
3946
- });
3947
- }
3948
- function ensureRightHandRule(polygon) {
3949
- // Ensure the outer ring follows the right-hand rule
3950
- if (polygon.coordinates.length > 0) {
3951
- const outerRing = polygon.coordinates[0];
3952
- if (isClockwise(outerRing)) {
3953
- polygon.coordinates[0] = outerRing.reverse();
3954
- }
3955
- }
3956
- // Ensure any inner rings (holes) follow the right-hand rule
3957
- for (let i = 1; i < polygon.coordinates.length; i++) {
3958
- const innerRing = polygon.coordinates[i];
3959
- if (!isClockwise(innerRing)) {
3960
- polygon.coordinates[i] = innerRing.reverse();
3961
- }
3962
- }
3963
- }
3964
- function isClockwise(ring) {
3965
- let sum = 0;
3966
- for (let i = 0; i < ring.length - 1; i++) {
3967
- const p1 = ring[i];
3968
- const p2 = ring[i + 1];
3969
- sum += (p2[0] - p1[0]) * (p2[1] + p1[1]);
3970
- }
3971
- return sum > 0;
3972
- }
3973
-
3974
- function OnNextCRender(viewer, callback) {
3975
- const removal = viewer.scene.postRender.addEventListener(() => {
3976
- removal();
3977
- callback();
3978
- });
3979
- }
3980
- function GetCValue(viewer, obj) {
3981
- if (obj === null || obj === void 0 ? void 0 : obj.getValue) {
3982
- let date = viewer.scene.lastRenderTime;
3983
- if (!date) {
3984
- date = viewer.clock.currentTime;
3985
- }
3986
- return obj.getValue(date);
3987
- }
3988
- return obj;
3989
- }
3990
- /*
3991
- * Marks an EntityCollection as one this library created and fully controls (a menu item's own data source).
3992
- */
3993
- const OWNED_CCOLLECTION_KEY = "_bruceOwnedCollection";
3994
- function MarkCCollectionOwned(collection) {
3995
- if (collection) {
3996
- collection[OWNED_CCOLLECTION_KEY] = true;
3997
- }
3998
- }
3999
- function IsCCollectionOwned(collection) {
4000
- return Boolean(collection && collection[OWNED_CCOLLECTION_KEY]);
4001
- }
4002
- /**
4003
- * Runs a bulk collection mutation with every given collection's events suspended, so Cesium raises one
4004
- * collectionChanged per collection for the whole pass instead of one per entity.
4005
- *
4006
- * Takes a set of collections rather than assuming `viewer.entities`, because managed visuals live in their menu
4007
- * item's own data source: suspending the wrong collection silently does nothing.
4008
- * @param collections Duplicates and empty entries are ignored.
4009
- * @param run
4010
- */
4011
- function WithSuspendedCCollections(collections, run) {
4012
- const unique = [];
4013
- for (const collection of collections) {
4014
- if (collection && unique.indexOf(collection) === -1) {
4015
- unique.push(collection);
4016
- }
4017
- }
4018
- for (const collection of unique) {
4019
- collection.suspendEvents();
4020
- }
4021
- try {
4022
- return run();
4023
- }
4024
- finally {
4025
- for (const collection of unique) {
4026
- try {
4027
- collection.resumeEvents();
4028
- }
4029
- catch (e) {
4030
- console.error(e);
4031
- }
4032
- }
4033
- }
4034
- }
4035
- /**
4036
- * Returns the collection a graphic belongs to, falling back to the viewer's own when it has never been added.
4037
- * @param viewer
4038
- * @param entity
4039
- */
4040
- function GetCEntityCollection(viewer, entity) {
4041
- const owner = entity === null || entity === void 0 ? void 0 : entity.entityCollection;
4042
- return owner ? owner : viewer === null || viewer === void 0 ? void 0 : viewer.entities;
4043
- }
4044
- /**
4045
- * Returns whether a graphic is currently in the scene, looking in whichever collection owns it.
4046
- * @param viewer
4047
- * @param entity
4048
- */
4049
- function IsCEntityInScene(viewer, entity) {
4050
- const collection = GetCEntityCollection(viewer, entity);
4051
- return Boolean(collection && entity && collection.contains(entity));
4052
- }
4053
- /**
4054
- * Removes a graphic from whichever collection owns it. Returns whether it removed anything.
4055
- * @param viewer
4056
- * @param entity
4057
- */
4058
- function RemoveCEntityFromScene(viewer, entity) {
4059
- var _a;
4060
- if (!entity) {
4061
- return false;
4062
- }
4063
- const collection = GetCEntityCollection(viewer, entity);
4064
- if (collection === null || collection === void 0 ? void 0 : collection.contains(entity)) {
4065
- return collection.remove(entity);
4066
- }
4067
- // A stale owner reference and the graphic ended up in the viewer's collection anyway.
4068
- if (collection !== (viewer === null || viewer === void 0 ? void 0 : viewer.entities) && ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.entities) === null || _a === void 0 ? void 0 : _a.contains(entity))) {
4069
- return viewer.entities.remove(entity);
4070
- }
4071
- return false;
4072
- }
4073
- /**
4074
- * Adds a graphic back into whichever collection owns it, or the viewer's if it has no owner yet.
4075
- * @param viewer
4076
- * @param entity
4077
- */
4078
- function AddCEntityToScene(viewer, entity, fallbackCollection) {
4079
- if (!entity) {
4080
- return false;
4081
- }
4082
- // An existing owner always wins, so a graphic is never migrated out from under whoever holds it.
4083
- const owner = entity.entityCollection;
4084
- const collection = owner ? owner : (fallbackCollection ? fallbackCollection : viewer === null || viewer === void 0 ? void 0 : viewer.entities);
4085
- if (!collection || collection.contains(entity)) {
4086
- return false;
4087
- }
4088
- collection.add(entity);
4089
- return true;
4090
- }
4091
- /**
4092
- * Returns whether toggling `show` on these graphics is safe, rather than having to add/remove them from the
4093
- * entity collection to hide them.
4094
- *
4095
- * Toggling show is far cheaper than re-adding the graphic to the viewer however a Cesium bug means we can't always use it.
4096
- * See: https://github.com/CesiumGS/cesium/issues/9370
4097
- * @param parts The graphic and any sibling graphics, eg. from EntityUtils.GatherEntity.
4098
- */
4099
- function CanToggleShowSafely(parts) {
4100
- if (!(parts === null || parts === void 0 ? void 0 : parts.length)) {
4101
- return false;
4102
- }
4103
- for (const part of parts) {
4104
- if (!(part instanceof Cesium.Entity)) {
4105
- // Not an Entity, so not something whose `show` we can drive.
4106
- return false;
4107
- }
4108
- if (part.polyline && isTruthyCValue(part.polyline.clampToGround)) {
4109
- return false;
4110
- }
4111
- const materialGraphics = [part.polygon, part.corridor, part.ellipse, part.rectangle, part.wall, part.polylineVolume];
4112
- for (const graphic of materialGraphics) {
4113
- if (graphic && !isPlainColorMaterial(graphic.material)) {
4114
- return false;
4115
- }
4116
- }
4117
- }
4118
- return true;
4119
- }
4120
- /**
4121
- * Returns whether a material is a plain colour, which is what keeps a graphic out of Cesium's per-material batches.
4122
- * Anything unrecognised counts as not plain, so callers stay on the conservative side.
4123
- * @param material
4124
- */
4125
- function isPlainColorMaterial(material) {
4126
- if (material == null) {
4127
- // Cesium defaults an absent material to white, a plain colour.
4128
- return true;
4129
- }
4130
- return material instanceof Cesium.ColorMaterialProperty || material instanceof Cesium.Color;
4131
- }
4132
- /**
4133
- * Resolves a possibly-property boolean without needing a viewer clock, treating anything undeterminable as true
4134
- * so callers stay on the conservative side.
4135
- * @param value
4136
- */
4137
- function isTruthyCValue(value) {
4138
- if (value == null) {
4139
- return false;
4140
- }
4141
- if (typeof value.getValue === "function") {
4142
- if (value instanceof Cesium.ConstantProperty) {
4143
- return Boolean(value.valueOf());
4144
- }
4145
- // A non-constant property could be true at any moment, so assume it is.
4146
- return true;
4147
- }
4148
- return Boolean(value);
4149
- }
4150
- /**
4151
- * Returns if two resolved Cesium property values are equal, using the value's own equals() when it has one.
4152
- * @param a
4153
- * @param b
4154
- */
4155
- function AreCValuesEqual(a, b) {
4156
- if (a === b) {
4157
- return true;
4158
- }
4159
- // One side is null/undefined and the other isn't.
4160
- if (a == null || b == null) {
4161
- return false;
4162
- }
4163
- if (typeof a.equals === "function") {
4164
- return a.equals(b) === true;
4165
- }
4166
- return false;
4167
- }
4168
- /**
4169
- * Wraps a value in a ConstantProperty and assigns it to a graphic's property, but only when the resolved
4170
- * value actually differs. Returns whether it assigned.
4171
- * @param params
4172
- */
4173
- function SetCPropIfChanged(params) {
4174
- const { viewer, graphic, property, value, compare } = params;
4175
- if (!graphic) {
4176
- return false;
4177
- }
4178
- const oldValue = GetCValue(viewer, graphic[property]);
4179
- const isEqual = compare ? compare(oldValue, value) : AreCValuesEqual(oldValue, value);
4180
- if (isEqual) {
4181
- return false;
4182
- }
4183
- graphic[property] = new Cesium.ConstantProperty(value);
4184
- return true;
4185
- }
4186
- function ColorToCColor(color) {
4187
- return new Cesium.Color(color.red ? color.red / 255 : 0, color.green ? color.green / 255 : 0, color.blue ? color.blue / 255 : 0, color.alpha);
4188
- }
4189
- /**
4190
- * Removes duplicate points in a row and returns a new array.
4191
- * Passed array is not mutated.
4192
- * @param posses
4193
- */
4194
- function CullDuplicateCPosses(posses) {
4195
- const newPosses = [];
4196
- let lastPos = null;
4197
- for (let i = 0; i < posses.length; i++) {
4198
- const pos = posses[i];
4199
- // Avoiding 'Cesium.Cartesian3.equals' because passed data might be just a json blob in certain cases.
4200
- if (pos && (!lastPos || (pos.x != lastPos.x || pos.y != lastPos.y || pos.z != lastPos.z))) {
4201
- newPosses.push(pos);
4202
- lastPos = pos;
4203
- }
4204
- }
4205
- return newPosses;
4206
- }
4207
- /**
4208
- * Returns if the given positions are equal.
4209
- * @param a
4210
- * @param b
4211
- */
4212
- function CompareCPosses(a, b) {
4213
- // Same reference.
4214
- if (a == b) {
4215
- return true;
4216
- }
4217
- // Different lengths or one is null.
4218
- if ((!a || !b) || (a.length != b.length)) {
4219
- return false;
4220
- }
4221
- for (let i = 0; i < a.length; i++) {
4222
- const posA = a[i];
4223
- const posB = b[i];
4224
- // Null detected.
4225
- if (!posA || !posB) {
4226
- return false;
4227
- }
4228
- // Position values don't match.
4229
- if (!BModels.Cartes.IsEqualCartes3(posA, posB)) {
4230
- return false;
4231
- }
4232
- }
4233
- return true;
4234
- }
4235
- /**
4236
- * Returns if the given polygon hierarchies are equal.
4237
- * @param a
4238
- * @param b
4239
- */
4240
- function CompareCPolygonHierarchies(a, b) {
4241
- // Same reference.
4242
- if (a == b) {
4243
- return true;
4244
- }
4245
- // Different lengths or one is null.
4246
- if ((!a || !b) || (a.positions.length != b.positions.length)) {
4247
- return false;
4248
- }
4249
- // Positions don't match.
4250
- if (!CompareCPosses(a.positions, b.positions)) {
4251
- return false;
4252
- }
4253
- const holes = a.holes;
4254
- const bHoles = b.holes;
4255
- // Number of holes don't match.
4256
- if (holes.length != bHoles.length) {
4257
- return false;
4258
- }
4259
- for (let i = 0; i < holes.length; i++) {
4260
- // Hole positions don't match.
4261
- if (!CompareCPosses(holes[i].positions, bHoles[i].positions)) {
4262
- return false;
4263
- }
4264
- }
4265
- return true;
4266
- }
4267
-
4268
- const CESIUM_INSPECTOR_KEY = "_nextspace_inspector";
4269
- const CESIUM_TIMELINE_KEY = "_nextspace_timeline";
4270
- const CESIUM_TIMELINE_LIVE_KEY = "_nextspace_timeline_live";
4271
- const CESIUM_TIMELINE_LIVE_PADDING_KEY = "_nextspace_timeline_live_padding";
4272
- const CESIUM_TIMELINE_INTERVAL_KEY = "_nextspace_timeline_interval";
4273
- const CESIUM_MODEL_SPACE_KEY = "_nextspace_model_space";
4274
- const DEFAULT_LIVE_PADDING_SECONDS = 30 * 60;
4275
- (function (ViewUtils) {
4276
- function GatherLegacyMapTiles(params) {
4277
- const viewer = params.viewer;
4278
- const collection = viewer.imageryLayers;
4279
- const tiles = [];
4280
- for (let i = 0; i < collection.length; i++) {
4281
- const layer = collection.get(i);
4282
- if (layer._bName) {
4283
- tiles.push({
4284
- alpha: layer.alpha,
4285
- brightness: layer.brightness,
4286
- contrast: layer.contrast,
4287
- hue: layer.hue,
4288
- saturation: layer.saturation,
4289
- gamma: layer.gamma,
4290
- title: layer._bName,
4291
- });
4292
- }
4293
- }
4294
- return {
4295
- imagery: tiles.reverse()
4296
- };
4297
- }
4298
- ViewUtils.GatherLegacyMapTiles = GatherLegacyMapTiles;
4299
- function GatherLegacyTerrainTile(params) {
4300
- const viewer = params.viewer;
4301
- const enabled = viewer.terrainProvider;
4302
- if (enabled === null || enabled === void 0 ? void 0 : enabled._bName) {
4303
- return {
4304
- terrain: enabled._bName
4305
- };
4306
- }
4307
- return {
4308
- terrain: "flatterrain"
4309
- };
4310
- }
4311
- ViewUtils.GatherLegacyTerrainTile = GatherLegacyTerrainTile;
4312
- function GatherMapTiles(params) {
4313
- const viewer = params.viewer;
4314
- const imagery = viewer.imageryLayers;
4315
- const tiles = [];
4316
- for (let i = 0; i < imagery.length; i++) {
4317
- const provider = imagery.get(i);
4318
- if (provider._bMeta) {
4319
- const idCombo = provider._bMeta.accountId + provider._bMeta.tilesetId;
4320
- if (!tiles.find(x => x.accountId + x.tilesetId === idCombo)) {
4321
- tiles.push({
4322
- accountId: provider._bMeta.accountId,
4323
- tilesetId: provider._bMeta.tilesetId,
4324
- alpha: provider.alpha,
4325
- brightness: provider.brightness,
4326
- contrast: provider.contrast,
4327
- hue: provider.hue,
4328
- saturation: provider.saturation,
4329
- gamma: provider.gamma
4330
- });
4331
- }
4332
- }
4333
- }
4334
- return {
4335
- imagery: tiles
4336
- };
4337
- }
4338
- ViewUtils.GatherMapTiles = GatherMapTiles;
4339
- function GatherTerrainTile(params) {
4340
- const viewer = params.viewer;
4341
- const provider = viewer.terrainProvider;
4342
- if (provider === null || provider === void 0 ? void 0 : provider._bMeta) {
4343
- return {
4344
- terrain: {
4345
- accountId: provider._bMeta.accountId,
4346
- tilesetId: provider._bMeta.tilesetId,
4347
- }
4348
- };
4349
- }
4350
- else if (provider instanceof Cesium.EllipsoidTerrainProvider) {
4351
- return {
4352
- terrain: {
4353
- tilesetId: BModels.ProjectViewTile.EDefaultTerrain.FlatTerrain,
4354
- accountId: null
4355
- }
4356
- };
4357
- }
4358
- return null;
4359
- }
4360
- ViewUtils.GatherTerrainTile = GatherTerrainTile;
4361
- function SetTerrainWireframeStatus(params) {
4362
- if (!params.viewer[CESIUM_INSPECTOR_KEY]) {
4363
- const InspectorClass = Cesium.CesiumInspector;
4364
- if (InspectorClass) {
4365
- const inspector = new InspectorClass(document.createElement("div"), params.viewer.scene);
4366
- inspector.container.style.display = "none";
4367
- params.viewer[CESIUM_INSPECTOR_KEY] = inspector;
4368
- params.viewer.scene.requestRender();
4369
- }
4370
- }
4371
- if (params.viewer[CESIUM_INSPECTOR_KEY]) {
4372
- params.viewer[CESIUM_INSPECTOR_KEY].viewModel.wireframe = params.status;
4373
- }
4374
- }
4375
- ViewUtils.SetTerrainWireframeStatus = SetTerrainWireframeStatus;
4376
- function GetTerrainWireframeStatus(params) {
4377
- var _a, _b;
4378
- if (!params.viewer[CESIUM_INSPECTOR_KEY]) {
4379
- return false;
4380
- }
4381
- return (_b = (_a = params.viewer[CESIUM_INSPECTOR_KEY]) === null || _a === void 0 ? void 0 : _a.viewModel) === null || _b === void 0 ? void 0 : _b.wireframe;
4382
- }
4383
- ViewUtils.GetTerrainWireframeStatus = GetTerrainWireframeStatus;
4384
- /**
4385
- * Changes between perspective and orthographic view.
4386
- * When Cesium stops being bad at picking positions in 2d mode we'll use the flat earth mode instead.
4387
- * @param params
4388
- */
4389
- function Set2dStatus(params) {
4390
- const { viewer, status: is2d, moveCamera } = params;
4391
- const curLens2d = viewer.camera.frustum instanceof Cesium.OrthographicFrustum;
4392
- if (curLens2d && !is2d) {
4393
- OnNextCRender(viewer, () => {
4394
- viewer.camera.switchToPerspectiveFrustum();
4395
- viewer.scene.screenSpaceCameraController.enableTilt = true;
4396
- });
4397
- viewer.scene.requestRender();
4398
- }
4399
- else if (!curLens2d && is2d) {
4400
- OnNextCRender(viewer, () => {
4401
- viewer.camera.switchToOrthographicFrustum();
4402
- viewer.scene.screenSpaceCameraController.enableTilt = false;
4403
- });
4404
- if (moveCamera != false) {
4405
- try {
4406
- // Face camera downwards to make it look 2d.
4407
- // We want to try make it look at the center-point of the current view.
4408
- // If center cannot be calculated then we'll simply raise the camera and face it downwards.
4409
- const scene = viewer.scene;
4410
- const windowPosition = new Cesium.Cartesian2(scene.canvas.clientWidth / 2, scene.canvas.clientHeight / 2);
4411
- const ray = viewer.camera.getPickRay(windowPosition);
4412
- const intersection = scene.globe.pick(ray, scene);
4413
- let center;
4414
- if (Cesium.defined(intersection)) {
4415
- center = Cesium.Cartographic.fromCartesian(intersection);
4416
- }
4417
- // Use current camera position if we can't calculate the center.
4418
- else {
4419
- center = Cesium.Cartographic.fromCartesian(viewer.camera.position);
4420
- center.height = 0;
4421
- }
4422
- center.height = viewer.camera.positionCartographic.height + 100;
4423
- viewer.camera.setView({
4424
- destination: Cesium.Cartographic.toCartesian(center),
4425
- orientation: {
4426
- heading: 0.0,
4427
- pitch: Cesium.Math.toRadians(-90.0),
4428
- roll: 0.0
4429
- }
4430
- });
4431
- }
4432
- catch (e) {
4433
- console.error(e);
4434
- }
4435
- }
4436
- viewer.scene.requestRender();
4437
- }
4438
- }
4439
- ViewUtils.Set2dStatus = Set2dStatus;
4440
- function Get2dStatus(params) {
4441
- const { viewer } = params;
4442
- return viewer.camera.frustum instanceof Cesium.OrthographicFrustum;
4443
- }
4444
- ViewUtils.Get2dStatus = Get2dStatus;
4445
- function SetLockedCameraStatus(params) {
4446
- const { viewer, status } = params;
4447
- const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
4448
- if (!scene) {
4449
- return;
4450
- }
4451
- if (status) {
4452
- scene.screenSpaceCameraController.enableInputs = false;
4453
- scene.screenSpaceCameraController.enableTranslate = false;
4454
- scene.screenSpaceCameraController.enableZoom = false;
4455
- scene.screenSpaceCameraController.enableRotate = false;
4456
- scene.screenSpaceCameraController.enableTilt = false;
4457
- }
4458
- else {
4459
- scene.screenSpaceCameraController.enableInputs = true;
4460
- scene.screenSpaceCameraController.enableTranslate = true;
4461
- scene.screenSpaceCameraController.enableZoom = true;
4462
- scene.screenSpaceCameraController.enableRotate = true;
4463
- if (!ViewUtils.Get2dStatus({ viewer })) {
4464
- scene.screenSpaceCameraController.enableTilt = true;
4465
- }
4466
- }
4467
- viewer.scene.requestRender();
4468
- }
4469
- ViewUtils.SetLockedCameraStatus = SetLockedCameraStatus;
4470
- function GetLockedCameraStatus(params) {
4471
- var _a;
4472
- const scene = (_a = params.viewer) === null || _a === void 0 ? void 0 : _a.scene;
4473
- if (!scene) {
4474
- return false;
4475
- }
4476
- return !scene.screenSpaceCameraController.enableTranslate;
4477
- }
4478
- ViewUtils.GetLockedCameraStatus = GetLockedCameraStatus;
4479
- /**
4480
- * Returns the current camera time and related settings.
4481
- * @param params
4482
- * @returns
4483
- */
4484
- function GetTimeDetails(params) {
4485
- const { viewer } = params;
4486
- const clock = viewer.clock;
4487
- return {
4488
- animationSpeed: clock.multiplier,
4489
- isAnimating: clock.shouldAnimate,
4490
- startTime: clock.startTime,
4491
- startTimeIso: clock.startTime.toString(),
4492
- currentTime: clock.currentTime,
4493
- currentTimeIso: clock.currentTime.toString(),
4494
- stopTime: clock.stopTime,
4495
- stopTimeIso: clock.stopTime.toString(),
4496
- areCesiumValues: clock[CESIUM_TIMELINE_KEY] != true,
4497
- isLive: clock[CESIUM_TIMELINE_LIVE_KEY] == true,
4498
- livePaddingSeconds: clock[CESIUM_TIMELINE_LIVE_PADDING_KEY] || DEFAULT_LIVE_PADDING_SECONDS
4280
+ }
4281
+ }
4282
+ else if (geometry.LineString) {
4283
+ count += BModels.Geometry.ParsePoints(geometry.LineString).length;
4284
+ }
4285
+ else if (geometry.Point) {
4286
+ count += 1;
4287
+ }
4499
4288
  };
4289
+ traverse(geometry);
4290
+ return count;
4500
4291
  }
4501
- ViewUtils.GetTimeDetails = GetTimeDetails;
4292
+ SimplifyGeometry.CountPoints = CountPoints;
4502
4293
  /**
4503
- * Sets the Cesium clock values.
4504
- * If a value is not provided, then 'areCesiumValues' checked to see if the values are Cesium values.
4505
- * If they're currently Cesium set values then our defaults are used.
4506
- * @param params
4294
+ * Simplifies input geometry.
4295
+ * This will turn it into GeoJSON, run it through turf, then back to Bruce geometry.
4296
+ * @param geometry
4507
4297
  */
4508
- function SetTimeDetails(params) {
4509
- let { viewer, animationSpeed, isAnimating, startTime, currentTime, stopTime, startTimeIso, stopTimeIso, currentTimeIso, isLive, livePaddingSeconds } = params;
4510
- const clock = viewer.clock;
4511
- if (startTime) {
4512
- clock.startTime = startTime;
4298
+ function Simplify(entityId, geometry, tolerance) {
4299
+ var _a;
4300
+ if (!geometry || !isTurfAvailable() || !turf.simplify) {
4301
+ return geometry;
4513
4302
  }
4514
- else if (startTimeIso) {
4515
- const startDate = Cesium.JulianDate.fromIso8601(startTimeIso);
4516
- if (startDate) {
4517
- clock.startTime = startDate;
4518
- }
4303
+ const original = geometry;
4304
+ // Convert to geojson so that we can interact with turf.
4305
+ const gFeature = BModels.Geometry.ToGeoJsonFeature({
4306
+ geometry: geometry
4307
+ });
4308
+ // Unkink the geometry.
4309
+ // This removes overlapping polygons before we start merging and simplifying.
4310
+ // Commented out because it has crashes and doesn't handle holes.
4311
+ untangleFeature(gFeature);
4312
+ // Union the geometry.
4313
+ // This merges overlapping polygons.
4314
+ unionFeature(gFeature);
4315
+ // Simplify the geometry.
4316
+ if (tolerance > 0) {
4317
+ gFeature.geometry = turf.simplify(gFeature.geometry, {
4318
+ tolerance: tolerance,
4319
+ highQuality: true,
4320
+ mutate: true
4321
+ });
4519
4322
  }
4520
- if (currentTime) {
4521
- clock.currentTime = currentTime;
4323
+ // Converting back to Bruce geometry.
4324
+ const simplified = (_a = BModels.Geometry.FromGeoJson({
4325
+ geoJson: gFeature
4326
+ })) === null || _a === void 0 ? void 0 : _a.geometry;
4327
+ // A polygon we cannot vouch for is worse than an unsimplified one, so fall back instead of shipping it.
4328
+ if (!simplified || !isSafeToRender(simplified)) {
4329
+ return original;
4522
4330
  }
4523
- else if (currentTimeIso) {
4524
- const currentDate = Cesium.JulianDate.fromIso8601(currentTimeIso);
4525
- if (currentDate) {
4526
- clock.currentTime = currentDate;
4527
- }
4331
+ return simplified;
4332
+ }
4333
+ SimplifyGeometry.Simplify = Simplify;
4334
+ /**
4335
+ * Whether a simplified geometry still describes something a renderer can triangulate.
4336
+ * @param geometry the simplification result.
4337
+ * @returns false when any polygon has a collapsed ring, a hole outside its outer ring, or a hole
4338
+ * wound the same way as its outer ring.
4339
+ */
4340
+ function IsSafeToRender(geometry) {
4341
+ return isSafeToRender(geometry);
4342
+ }
4343
+ SimplifyGeometry.IsSafeToRender = IsSafeToRender;
4344
+ })(SimplifyGeometry || (SimplifyGeometry = {}));
4345
+ // A ring needs three distinct corners plus the closing repeat before it encloses any area.
4346
+ const MIN_RING_POSITIONS = 4;
4347
+ function isSafeToRender(geometry) {
4348
+ let safe = true;
4349
+ const check = (g) => {
4350
+ if (!safe) {
4351
+ return;
4528
4352
  }
4529
- if (stopTime) {
4530
- clock.stopTime = stopTime;
4353
+ if (g === null || g === void 0 ? void 0 : g.MultiGeometry) {
4354
+ g.MultiGeometry.forEach(check);
4355
+ return;
4531
4356
  }
4532
- else if (stopTimeIso) {
4533
- const stopDate = Cesium.JulianDate.fromIso8601(stopTimeIso);
4534
- if (stopDate) {
4535
- clock.stopTime = stopDate;
4536
- }
4357
+ if (!(g === null || g === void 0 ? void 0 : g.Polygon) || g.Polygon.length === 0) {
4358
+ return;
4537
4359
  }
4538
- clock.clockRange = Cesium.ClockRange.LOOP_STOP;
4539
- const areCesiumValues = clock[CESIUM_TIMELINE_KEY] != true;
4540
- if (areCesiumValues) {
4541
- if (isNaN(animationSpeed) || animationSpeed == null) {
4542
- animationSpeed = 1;
4360
+ let outer = null;
4361
+ let outerIsClockwise = false;
4362
+ for (let i = 0; i < g.Polygon.length; i++) {
4363
+ const points = BModels.Geometry.ParsePoints(g.Polygon[i].LinearRing);
4364
+ if (!points || points.length < MIN_RING_POSITIONS) {
4365
+ safe = false;
4366
+ return;
4543
4367
  }
4544
- if (isAnimating == null) {
4545
- isAnimating = false;
4368
+ const clockwise = signedRingArea(points) < 0;
4369
+ if (g.Polygon[i].Facing !== "in") {
4370
+ outer = points;
4371
+ outerIsClockwise = clockwise;
4372
+ continue;
4546
4373
  }
4547
- if (isLive == null) {
4548
- isLive = false;
4374
+ // A hole with no outer ring ahead of it cannot be subtracted from anything.
4375
+ if (!outer) {
4376
+ safe = false;
4377
+ return;
4549
4378
  }
4550
- if (livePaddingSeconds == null) {
4551
- livePaddingSeconds = DEFAULT_LIVE_PADDING_SECONDS;
4379
+ if (clockwise === outerIsClockwise) {
4380
+ safe = false;
4381
+ return;
4382
+ }
4383
+ for (let j = 0; j < points.length; j++) {
4384
+ if (!isPointInRing(points[j], outer)) {
4385
+ safe = false;
4386
+ return;
4387
+ }
4552
4388
  }
4553
4389
  }
4554
- if (!isNaN(animationSpeed) && animationSpeed != null) {
4555
- clock.multiplier = animationSpeed;
4390
+ };
4391
+ check(geometry);
4392
+ return safe;
4393
+ }
4394
+ function signedRingArea(points) {
4395
+ let sum = 0;
4396
+ for (let i = 0; i < points.length; i++) {
4397
+ const a = points[i];
4398
+ const b = points[(i + 1) % points.length];
4399
+ sum += a.longitude * b.latitude - b.longitude * a.latitude;
4400
+ }
4401
+ return sum / 2;
4402
+ }
4403
+ function isPointInRing(point, ring) {
4404
+ let inside = false;
4405
+ for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
4406
+ const a = ring[i];
4407
+ const b = ring[j];
4408
+ if ((a.latitude > point.latitude) !== (b.latitude > point.latitude)
4409
+ && point.longitude < (b.longitude - a.longitude) * (point.latitude - a.latitude) / (b.latitude - a.latitude) + a.longitude) {
4410
+ inside = !inside;
4556
4411
  }
4557
- if (isAnimating != null) {
4558
- clock.shouldAnimate = isAnimating;
4412
+ }
4413
+ return inside;
4414
+ }
4415
+ /**
4416
+ * Runs through all found polygons and runs unkink through turf on them.
4417
+ * @param feature
4418
+ */
4419
+ function untangleFeature(feature) {
4420
+ // Collection to add to because the result might be multiple from an unkink.
4421
+ const collection = {
4422
+ geometries: [],
4423
+ type: "GeometryCollection"
4424
+ };
4425
+ // Runs a dedupe and unkink on the coordinates.
4426
+ // Returns if the coordinates were added to the collection.
4427
+ const processCoordinates = (coords) => {
4428
+ try {
4429
+ // Dedupe the coordinates to avoid issues with unkink.
4430
+ coords = dedupeCoordinates(coords);
4431
+ const outer = [];
4432
+ let inner = [];
4433
+ // We'll unkink each ring separately as this is killing the holes when it is done as a whole.
4434
+ // So we'll do it separate then recombine.
4435
+ for (let i = 0; i < coords.length; i++) {
4436
+ const ring = coords[i];
4437
+ const unkink = turf.unkinkPolygon({
4438
+ type: "Polygon",
4439
+ coordinates: [ring]
4440
+ });
4441
+ if (unkink.type == "FeatureCollection") {
4442
+ let target = i == 0 ? outer : inner;
4443
+ for (let j = 0; j < unkink.features.length; j++) {
4444
+ const unkinked = unkink.features[j].geometry.coordinates;
4445
+ if (unkinked.length > 0) {
4446
+ target.push(unkinked[0]);
4447
+ }
4448
+ }
4449
+ }
4450
+ else {
4451
+ console.error("Unexpected unkink result.", unkink);
4452
+ }
4453
+ }
4454
+ // Recreate the rings and reapply to the collection.
4455
+ if (outer.length > 0) {
4456
+ // Unkinking a self touching boundary splits it into pieces. Keeping whichever piece
4457
+ // happened to come back first can discard most of the shape, so keep the biggest one.
4458
+ let keep = outer[0];
4459
+ if (outer.length > 1) {
4460
+ let best = Math.abs(ringArea(keep));
4461
+ for (let i = 1; i < outer.length; i++) {
4462
+ const area = Math.abs(ringArea(outer[i]));
4463
+ if (area > best) {
4464
+ best = area;
4465
+ keep = outer[i];
4466
+ }
4467
+ }
4468
+ }
4469
+ const combinedCoords = [keep];
4470
+ for (let i = 0; i < inner.length; i++) {
4471
+ // A hole belonging to a piece that was just discarded is no longer inside anything,
4472
+ // and a hole crossing the boundary is what tears the fill.
4473
+ if (isRingInsideRing(inner[i], keep)) {
4474
+ combinedCoords.push(inner[i]);
4475
+ }
4476
+ }
4477
+ // Add the combined coordinates to the collection
4478
+ const polygon = {
4479
+ type: "Polygon",
4480
+ coordinates: combinedCoords
4481
+ };
4482
+ // Ensure right-hand rule is followed.
4483
+ ensureRightHandRule(polygon);
4484
+ // Add to the collection.
4485
+ collection.geometries.push(polygon);
4486
+ return true;
4487
+ }
4559
4488
  }
4560
- viewer.scene.requestRender();
4561
- clock[CESIUM_TIMELINE_KEY] = true;
4562
- if (livePaddingSeconds != null) {
4563
- clock[CESIUM_TIMELINE_LIVE_PADDING_KEY] = livePaddingSeconds || DEFAULT_LIVE_PADDING_SECONDS;
4489
+ catch (e) {
4490
+ // console.error("Failed to unkink polygon.", e);
4564
4491
  }
4565
- if (isLive != null) {
4566
- clock[CESIUM_TIMELINE_LIVE_KEY] = params.isLive == true;
4492
+ return false;
4493
+ };
4494
+ const processGeometry = (geometry) => {
4495
+ let added = false;
4496
+ if (geometry.type == "Polygon") {
4497
+ added = processCoordinates(geometry.coordinates);
4567
4498
  }
4568
- if (clock[CESIUM_TIMELINE_LIVE_KEY]) {
4569
- startLive(viewer);
4499
+ else if (geometry.type == "MultiPolygon") {
4500
+ for (let i = 0; i < geometry.coordinates.length; i++) {
4501
+ added = processCoordinates(geometry.coordinates[i]) || added;
4502
+ }
4570
4503
  }
4571
- else {
4572
- stopLive(viewer);
4504
+ if (!added) {
4505
+ // Adding original since we can't unkink it.
4506
+ collection.geometries.push(geometry);
4573
4507
  }
4508
+ };
4509
+ if (feature.geometry.type == "GeometryCollection") {
4510
+ feature.geometry.geometries.forEach((geometry) => {
4511
+ processGeometry(geometry);
4512
+ });
4574
4513
  }
4575
- ViewUtils.SetTimeDetails = SetTimeDetails;
4576
- function startLive(viewer) {
4577
- const doUpdate = () => {
4578
- if (!viewer || viewer.isDestroyed() || !viewer.scene) {
4579
- stopLive(viewer);
4580
- return;
4514
+ else {
4515
+ processGeometry(feature.geometry);
4516
+ }
4517
+ // Re-assign the geometry.
4518
+ if (collection.geometries.length == 1) {
4519
+ feature.geometry = collection.geometries[0];
4520
+ }
4521
+ else {
4522
+ feature.geometry = collection;
4523
+ }
4524
+ }
4525
+ /**
4526
+ * Runs through all found polygons and unions them using turf.
4527
+ * Failed unions are ignored and the original is kept.
4528
+ * @param feature
4529
+ */
4530
+ function unionFeature(feature) {
4531
+ // Collection to add to because the result might be multiple from an unkink.
4532
+ const collection = {
4533
+ geometries: [],
4534
+ type: "GeometryCollection"
4535
+ };
4536
+ // We'll turn each geometry into a feature and union them.
4537
+ const tmpCollection = {
4538
+ features: [],
4539
+ type: "FeatureCollection"
4540
+ };
4541
+ if (feature.geometry.type == "GeometryCollection") {
4542
+ feature.geometry.geometries.forEach((geometry) => {
4543
+ if (geometry.type == "Polygon") {
4544
+ tmpCollection.features.push({
4545
+ type: "Feature",
4546
+ geometry: geometry,
4547
+ properties: {}
4548
+ });
4581
4549
  }
4582
- // Get the current settings.
4583
- const tDetails = GetTimeDetails({ viewer });
4584
- const clock = viewer.clock;
4585
- if (clock) {
4586
- clock.shouldAnimate = true;
4587
- clock.multiplier = 1;
4588
- clock.currentTime = Cesium.JulianDate.now();
4589
- clock.startTime = Cesium.JulianDate.addSeconds(clock.currentTime, -tDetails.livePaddingSeconds, new Cesium.JulianDate());
4590
- clock.stopTime = Cesium.JulianDate.addSeconds(clock.currentTime, tDetails.livePaddingSeconds, new Cesium.JulianDate());
4591
- clock.clockRange = Cesium.ClockRange.UNBOUNDED;
4550
+ else if (geometry.type == "MultiPolygon") {
4551
+ for (let i = 0; i < geometry.coordinates.length; i++) {
4552
+ tmpCollection.features.push({
4553
+ type: "Feature",
4554
+ geometry: {
4555
+ type: "Polygon",
4556
+ coordinates: geometry.coordinates[i]
4557
+ },
4558
+ properties: {}
4559
+ });
4560
+ }
4592
4561
  }
4593
- };
4594
- if (viewer[CESIUM_TIMELINE_INTERVAL_KEY]) {
4595
- doUpdate();
4596
- return;
4597
- }
4598
- // We'll start an interval that occasionally updates the Cesium clock to stay in the desired settings.
4599
- // Since it moves live (1 second per second), we don't have to worry about the ranges being out of date quickly.
4600
- viewer[CESIUM_TIMELINE_INTERVAL_KEY] = setInterval(() => {
4601
- doUpdate();
4602
- }, 800);
4603
- viewer[CESIUM_TIMELINE_LIVE_KEY] = true;
4604
- // Initial update.
4605
- doUpdate();
4562
+ // Only focusing on polygons.
4563
+ else {
4564
+ collection.geometries.push(geometry);
4565
+ }
4566
+ });
4606
4567
  }
4607
- function stopLive(viewer) {
4608
- if (!viewer) {
4609
- return;
4610
- }
4611
- let interval = viewer[CESIUM_TIMELINE_INTERVAL_KEY];
4612
- if (interval) {
4613
- clearInterval(interval);
4614
- viewer[CESIUM_TIMELINE_INTERVAL_KEY] = null;
4568
+ else if (feature.geometry.type == "Polygon") {
4569
+ tmpCollection.features.push({
4570
+ type: "Feature",
4571
+ geometry: feature.geometry,
4572
+ properties: {}
4573
+ });
4574
+ }
4575
+ else if (feature.geometry.type == "MultiPolygon") {
4576
+ for (let i = 0; i < feature.geometry.coordinates.length; i++) {
4577
+ tmpCollection.features.push({
4578
+ type: "Feature",
4579
+ geometry: {
4580
+ type: "Polygon",
4581
+ coordinates: feature.geometry.coordinates[i]
4582
+ },
4583
+ properties: {}
4584
+ });
4615
4585
  }
4616
- viewer[CESIUM_TIMELINE_LIVE_KEY] = false;
4617
4586
  }
4618
- function SetGlobeDetails(params) {
4619
- let { viewer, hidden, baseColor } = params;
4620
- const scene = viewer === null || viewer === void 0 ? void 0 : viewer.scene;
4621
- if (!(scene === null || scene === void 0 ? void 0 : scene.globe)) {
4622
- return;
4587
+ // No polygons to union.
4588
+ // Need at least 2 to union.
4589
+ if (tmpCollection.features.length < 2) {
4590
+ return;
4591
+ }
4592
+ // Now we'll union them.
4593
+ try {
4594
+ const union = turf.union(tmpCollection);
4595
+ if (union.geometry.type == "Polygon") {
4596
+ collection.geometries.push(union.geometry);
4623
4597
  }
4624
- if (hidden == null) {
4625
- hidden = !scene.globe.show;
4598
+ else if (union.geometry.type == "MultiPolygon") {
4599
+ for (let i = 0; i < union.geometry.coordinates.length; i++) {
4600
+ collection.geometries.push({
4601
+ type: "Polygon",
4602
+ coordinates: union.geometry.coordinates[i]
4603
+ });
4604
+ }
4626
4605
  }
4627
4606
  else {
4628
- scene.globe.show = !hidden;
4629
- }
4630
- if (baseColor == null) {
4631
- baseColor = scene.globe.baseColor;
4607
+ // Returning early because the result is unexpected.
4608
+ return;
4632
4609
  }
4633
- // When globe is off, we'll also hide the stars/moon.
4634
- // We also grab the globe color and apply it to the sky.
4635
- if (hidden && GetModelSpace(viewer)) {
4636
- scene.skyBox.show = false;
4637
- scene.skyAtmosphere.show = false;
4638
- scene.sun.show = false;
4639
- scene.moon.show = false;
4640
- scene.backgroundColor = baseColor.clone();
4610
+ }
4611
+ catch (e) {
4612
+ // console.error("Failed to union polygons.", e);
4613
+ return;
4614
+ }
4615
+ // Re-assign the geometry.
4616
+ if (collection.geometries.length == 1) {
4617
+ feature.geometry = collection.geometries[0];
4618
+ }
4619
+ else {
4620
+ feature.geometry = collection;
4621
+ }
4622
+ }
4623
+ function ringArea(ring) {
4624
+ let sum = 0;
4625
+ for (let i = 0; i < ring.length; i++) {
4626
+ const a = ring[i];
4627
+ const b = ring[(i + 1) % ring.length];
4628
+ sum += a[0] * b[1] - b[0] * a[1];
4629
+ }
4630
+ return sum / 2;
4631
+ }
4632
+ function isRingInsideRing(inner, outer) {
4633
+ for (let i = 0; i < inner.length; i++) {
4634
+ let inside = false;
4635
+ for (let a = 0, b = outer.length - 1; a < outer.length; b = a++) {
4636
+ if ((outer[a][1] > inner[i][1]) !== (outer[b][1] > inner[i][1])
4637
+ && inner[i][0] < (outer[b][0] - outer[a][0]) * (inner[i][1] - outer[a][1]) / (outer[b][1] - outer[a][1]) + outer[a][0]) {
4638
+ inside = !inside;
4639
+ }
4641
4640
  }
4642
- else {
4643
- scene.skyBox.show = true;
4644
- scene.skyAtmosphere.show = true;
4645
- scene.sun.show = true;
4646
- scene.moon.show = true;
4647
- scene.backgroundColor = Cesium.Color.BLACK;
4641
+ if (!inside) {
4642
+ return false;
4648
4643
  }
4649
- scene.globe.baseColor = baseColor;
4650
4644
  }
4651
- ViewUtils.SetGlobeDetails = SetGlobeDetails;
4652
- function SetModelSpace(viewer, modelSpace) {
4653
- if (!viewer) {
4654
- return;
4645
+ return true;
4646
+ }
4647
+ function dedupeCoordinates(inputCoords) {
4648
+ return inputCoords.map(ring => {
4649
+ if (ring.length < 2) {
4650
+ return ring;
4655
4651
  }
4656
- if (viewer[CESIUM_MODEL_SPACE_KEY] === modelSpace) {
4657
- return;
4652
+ const dedupeCoords = [];
4653
+ const uniqueCoords = new Set();
4654
+ // Dedupe the points.
4655
+ ring.forEach(coord => {
4656
+ const key = coord.join(',');
4657
+ if (!uniqueCoords.has(key)) {
4658
+ uniqueCoords.add(key);
4659
+ dedupeCoords.push(coord);
4660
+ }
4661
+ });
4662
+ // Make sure the last point matches the first.
4663
+ if (dedupeCoords.length > 1) {
4664
+ if (dedupeCoords[0][0] != dedupeCoords[dedupeCoords.length - 1][0] ||
4665
+ dedupeCoords[0][1] != dedupeCoords[dedupeCoords.length - 1][1]) {
4666
+ dedupeCoords.push(dedupeCoords[0]);
4667
+ }
4668
+ }
4669
+ return dedupeCoords;
4670
+ });
4671
+ }
4672
+ function ensureRightHandRule(polygon) {
4673
+ // Ensure the outer ring follows the right-hand rule
4674
+ if (polygon.coordinates.length > 0) {
4675
+ const outerRing = polygon.coordinates[0];
4676
+ if (isClockwise(outerRing)) {
4677
+ polygon.coordinates[0] = outerRing.reverse();
4658
4678
  }
4659
- viewer[CESIUM_MODEL_SPACE_KEY] = modelSpace;
4660
- // Reload globe since we display it differently between the two modes.
4661
- SetGlobeDetails({ viewer });
4662
4679
  }
4663
- ViewUtils.SetModelSpace = SetModelSpace;
4664
- function GetModelSpace(viewer) {
4665
- if (!viewer) {
4666
- return false;
4680
+ // Ensure any inner rings (holes) follow the right-hand rule
4681
+ for (let i = 1; i < polygon.coordinates.length; i++) {
4682
+ const innerRing = polygon.coordinates[i];
4683
+ if (!isClockwise(innerRing)) {
4684
+ polygon.coordinates[i] = innerRing.reverse();
4667
4685
  }
4668
- return Boolean(viewer[CESIUM_MODEL_SPACE_KEY]);
4669
4686
  }
4670
- ViewUtils.GetModelSpace = GetModelSpace;
4671
- })(exports.ViewUtils || (exports.ViewUtils = {}));
4687
+ }
4688
+ function isClockwise(ring) {
4689
+ let sum = 0;
4690
+ for (let i = 0; i < ring.length - 1; i++) {
4691
+ const p1 = ring[i];
4692
+ const p2 = ring[i + 1];
4693
+ sum += (p2[0] - p1[0]) * (p2[1] + p1[1]);
4694
+ }
4695
+ return sum > 0;
4696
+ }
4672
4697
 
4673
4698
  /*
4674
4699
  * Lets something decide WHEN a `show` flag is honoured, without owning WHAT it should be.
@@ -28952,6 +28977,7 @@
28952
28977
  light.color = Cesium.Color.fromCssColorString(lighting.color);
28953
28978
  light.intensity = EnsureNumber(lighting.intensity);
28954
28979
  }
28980
+ exports.ViewUtils.ApplyDefaultBrightness(viewer, EnsureNumber(lighting.brightnessShift, DEFAULT_BRIGHTNESS_SHIFT));
28955
28981
  let quality = bSettings === null || bSettings === void 0 ? void 0 : bSettings.quality;
28956
28982
  if (quality == null) {
28957
28983
  quality = (_x = defaults === null || defaults === void 0 ? void 0 : defaults.settings) === null || _x === void 0 ? void 0 : _x.quality;
@@ -35137,6 +35163,8 @@
35137
35163
  viewer
35138
35164
  });
35139
35165
  }
35166
+ // Newer cesium is much dimmer by default so we shift brightness a bit.
35167
+ exports.ViewUtils.ApplyDefaultBrightness(viewer);
35140
35168
  backgroundRendering(viewer);
35141
35169
  // Capturing the destroy call to destroy all widgets.
35142
35170
  const olDestroy = viewer.destroy;
@@ -43376,7 +43404,7 @@ void main() {
43376
43404
  StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
43377
43405
  })(exports.StyleUtils || (exports.StyleUtils = {}));
43378
43406
 
43379
- const VERSION = "7.1.6";
43407
+ const VERSION = "7.1.7";
43380
43408
  /**
43381
43409
  * Updates the environment instance used by bruce-cesium to one specified.
43382
43410
  * This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
@@ -43407,6 +43435,7 @@ void main() {
43407
43435
  exports.CESIUM_TIMELINE_INTERVAL_KEY = CESIUM_TIMELINE_INTERVAL_KEY;
43408
43436
  exports.CESIUM_MODEL_SPACE_KEY = CESIUM_MODEL_SPACE_KEY;
43409
43437
  exports.DEFAULT_LIVE_PADDING_SECONDS = DEFAULT_LIVE_PADDING_SECONDS;
43438
+ exports.DEFAULT_BRIGHTNESS_SHIFT = DEFAULT_BRIGHTNESS_SHIFT;
43410
43439
  exports.Draw3dPolygon = Draw3dPolygon;
43411
43440
  exports.Draw3dPolyline = Draw3dPolyline;
43412
43441
  exports.WidgetControlViewBarSearch = WidgetControlViewBarSearch;