diff --git a/public/index.html b/public/index.html index 10807a7ec4c88601a834bdc2bec8257520aeead7..789d4ecba0c66ed2c656b75c7ce0c902c420fecf 100644 --- a/public/index.html +++ b/public/index.html @@ -65,6 +65,9 @@ <!-- vanillaSelectBox --> <link href="./css/thirdparty/vanillaSelectBox.css" rel="stylesheet" /> + <!-- SweetAlert --> + <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script> + <!-- Custom JS --> <script defer type="module" src="./js/appCesium.js"></script> diff --git a/public/js/appCesium.js b/public/js/appCesium.js index c32da2298dd25602b5be478336b3f2f39c52886e..9e77d930e383c3fab47460546ee5a2608825d759 100644 --- a/public/js/appCesium.js +++ b/public/js/appCesium.js @@ -19,24 +19,44 @@ const viewer = new Cesium.Viewer("cesiumGlobeContainer", { }); /** - * Load and zoom to the extents of 3DTiles + * Add a tileset containing buildings + * * @param {String} urlTiles URL of the 3DTiles to be loaded - * @returns {undefined} undefined + * @returns {Object} The tileset dataset */ -const loadTiles = function (urlTiles) { - const tileset = new Cesium.Cesium3DTileset({ +const createBuildingTileset = function (urlTiles) { + return new Cesium.Cesium3DTileset({ url: urlTiles, }); - viewer.scene.primitives.add(tileset); +}; + +// 3D tileset with all buildings +const nonDetailedBuilding225Tileset = createBuildingTileset( + "data_3d/3dtiles/1_full/tileset.json" +); + +// 3D tileset with all buildings except Bau 225 +const detailedBuilding225Tileset = createBuildingTileset( + "data_3d/3dtiles/2_partial/tileset.json" +); - tileset.readyPromise.then(function () { +/** + * Load and zoom to the extents of 3DTiles + * @param {Object} buildingTileset The 3D tileset to be loaded + * @returns {undefined} undefined + */ +const loadTiles = function (buildingTileset) { + // Add the tileset to the viewer + viewer.scene.primitives.add(buildingTileset); + + buildingTileset.readyPromise.then(function () { viewer .zoomTo( - tileset, + buildingTileset, new Cesium.HeadingPitchRange( 0.0, -0.5, - tileset.boundingSphere.radius / 0.5 + buildingTileset.boundingSphere.radius / 0.5 ) ) .otherwise(function (err) { @@ -59,11 +79,8 @@ const loadTiles = function (urlTiles) { * @returns {undefined} undefined */ const loadNonDetailedBuilding225 = function () { - // Paths to data sources - const URL_3DTILES = "data_3d/3dtiles/1_full/tileset.json"; - - // Tileset with all buildings - loadTiles(URL_3DTILES); + // 3D tileset with all buildings + loadTiles(nonDetailedBuilding225Tileset); }; /** @@ -93,12 +110,11 @@ const gltfLoad = function (gltfUrl, gltfId) { * @returns {undefined} undefined */ const loadDetailedBuilding225 = function () { - // Paths to data sources - const URL_3DTILES = "data_3d/3dtiles/2_partial/tileset.json"; + // Path to glTF data source const URL_GLTF = "data_3d/gltf"; // Tileset without building 225 - loadTiles(URL_3DTILES); + loadTiles(detailedBuilding225Tileset); // Load Building 225 gltfLoad(URL_GLTF, "bosch_si225_3"); @@ -286,3 +302,74 @@ const activate3DTileFeaturePicking = function () { }; activate3DTileFeaturePicking(); + +/** + * Reset the styling for all buildings contained in a 3D tileset. The default colour is `white` + * + * @param {Object} targetTileset The 3D tileset to be styled + * @returns {undefined} undefined + */ +const resetStylingForAllBuildings = function (targetTileset) { + targetTileset.style = new Cesium.Cesium3DTileStyle({ + color: { + conditions: [["true", 'color("white")']], + }, + }); +}; + +/** + * Apply a different color to the buildings that are selected in the buildings & data points drop-down list + * + * @param {Object} targetTileset The 3D tileset to be styled + * @param {Array} buildingIdArr An array of building IDs + * @returns {undefined} undefined + */ +const applyStylingForSelectedBuildings = function ( + targetTileset, + buildingIdArr +) { + targetTileset.style = new Cesium.Cesium3DTileStyle({ + color: { + conditions: [ + // The maximum number of buildings = seven + ["${_gebaeude} === " + buildingIdArr[0], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[1], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[2], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[3], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[4], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[5], 'color("red")'], + ["${_gebaeude} === " + buildingIdArr[6], 'color("red")'], + ["true", 'color("white")'], + ], + }, + }); +}; + +/** + * Highlight the building(s) that are selected in the buildings & data points drop-down list + * + * @param {Array} selectedBldgsDataPntsSmplngRateAbbrevNestedArr An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s) + * @returns {undefined} undefined + */ +const highlightSelectedBuildings = function ( + selectedBldgsDataPntsSmplngRateAbbrevNestedArr +) { + // The building ID is the first element + const buildingIdArr = selectedBldgsDataPntsSmplngRateAbbrevNestedArr.map( + (bldgDataPtSmplngRateAbbrev) => bldgDataPtSmplngRateAbbrev[0] + ); + + // Use a set to remove duplicates + const uniqueBuildingIdArr = [...new Set(buildingIdArr)]; + + applyStylingForSelectedBuildings( + nonDetailedBuilding225Tileset, + uniqueBuildingIdArr + ); +}; + +export { + nonDetailedBuilding225Tileset, + resetStylingForAllBuildings, + highlightSelectedBuildings, +}; diff --git a/public/js/appChart.js b/public/js/appChart.js index de1a5cf09344c929bda931cc28306ef5e7b6bd41..12807cac207b159ab4e7d32cfbfab559cdc672f1 100644 --- a/public/js/appChart.js +++ b/public/js/appChart.js @@ -40,6 +40,7 @@ import { checkIfSelectedBuildingDataPointsOptionsAreValid, checkIfSelectedAggregationOptionsAreValid, getAbbreviationsForSelectedOptionsFromAllDropDownLists, + createErrorPopupMessage, } from "./src_modules/dropDownListHelpers.mjs"; import { drawColumnChartBasedOnSelectedOptions } from "./src_modules/dropDownListChartColumn.mjs"; @@ -50,6 +51,12 @@ import { drawLineChartBasedOnSelectedOptions } from "./src_modules/dropDownListC import { drawScatterPlotBasedOnSelectedOptions } from "./src_modules/dropDownListChartScatterPlot.mjs"; +import { + nonDetailedBuilding225Tileset, + resetStylingForAllBuildings, + highlightSelectedBuildings, +} from "./appCesium.js"; + /** * Callback function that wraps the logic of populating the linked drop down lists. * Will run on `DOMContentLoaded` event @@ -70,6 +77,9 @@ const afterDocumentLoads = function () { */ const drawChartUsingSelectedOptions = async function () { try { + // Reset the styling of the 3D tileset, in case building(s) were highlighted + resetStylingForAllBuildings(nonDetailedBuilding225Tileset); + // Note: The chart type amd aggregation type + duration are the first and // third elements respectively, we have ignored the second and fourth elements const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] = @@ -94,6 +104,11 @@ const drawChartUsingSelectedOptions = async function () { const [[, , selectedSamplingRateAbbrev]] = selectedBuildingsDataPointsSamplingRateAbbrevNestedArr; + // Highlight the selected buildings + highlightSelectedBuildings( + selectedBuildingsDataPointsSamplingRateAbbrevNestedArr + ); + // User-specified start date and end date for aggregation - used by MULTIPLE chart types const AGGREGATION_START_DATE = "2020-01-01"; const AGGREGATION_STOP_DATE = "2020-12-31"; @@ -256,8 +271,8 @@ const drawChartUsingSelectedOptions = async function () { } catch (err) { console.error(err); - // Display a dialog window with the error message - alert(err); + // Display a popup that displays the error message + createErrorPopupMessage(err); } finally { // Enable the 'draw chart' button enableDrawChartButton(); diff --git a/public/js/src_modules/dropDownListHelpers.mjs b/public/js/src_modules/dropDownListHelpers.mjs index bb37a61cfd798d97a079b2723cf287b03189a1e4..1231b4ed419fef34f30f1267042ad3cdb1e8002e 100644 --- a/public/js/src_modules/dropDownListHelpers.mjs +++ b/public/js/src_modules/dropDownListHelpers.mjs @@ -471,6 +471,20 @@ const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function ( } }; +/** + * Create a pop up message (when an error is thrown) using the SweetAlert library + * + * @param {Object} errorObj An error object + * @returns {undefined} undefined + */ +const createErrorPopupMessage = function (errorObj) { + swal({ + title: "Something went wrong!", + text: `${errorObj.message}`, + icon: "error", + }); +}; + export { splitMultipleOptionsTextDelimitedBySlash, getSelectedOptionsFromAllDropDownLists, @@ -481,4 +495,5 @@ export { checkIfSelectedBuildingDataPointsOptionsAreValid, checkIfSelectedAggregationOptionsAreValid, getAbbreviationsForSelectedOptionsFromAllDropDownLists, + createErrorPopupMessage, };