Chapter 8: Debug and Validation Tools#
Welcome back to the loaders.gl-showcases
tutorial! In the previous chapter, we explored how the application integrates with ArcGIS to securely access private content. Now that we've covered how to load and display various 3D datasets, it's time to look at the tools the application provides to understand what's inside that data and check for potential issues. This is where the Debug and Validation Tools come in.
What are Debug and Validation Tools?#
Imagine you're looking at a complex 3D model on the map, but something doesn't look quite right. Maybe a texture seems stretched, some geometry appears distorted, or you suspect the data isn't structured optimally. Without the right tools, it's hard to figure out why.
As the concept description explains:
This system provides special features, mainly on the Debug page, to inspect the detailed properties of individual 3D tiles (like geometry, textures, and metadata) and check for potential issues. It's like a mechanic's toolkit and diagnostic scanner for the 3D models, allowing users to look under the hood and identify problems within the data itself.
These tools are designed to give developers and advanced users insights into the technical details of the 3D tiles being rendered and flag potential data quality issues.
The Central Use Case: Diagnosing a Visual Problem#
Let's say a building model loaded as a 3D tileset has a weird visual artifact on one specific part of its surface. You suspect it might be related to the geometry, the texture coordinates (UVs), or perhaps a problem with how that particular tile is defined (like its bounding volume or level of detail settings).
The central use case for Debug and Validation tools is to help you investigate this specific tile: 1. Identify the problematic tile. 2. Inspect its properties (vertex count, texture presence, etc.). 3. Visualize its internal structure (like seeing the wireframe or normals). 4. Run automated checks for common data issues on that tile.
By using these tools, you can gather clues to understand the root cause of the visual problem.
Key Concepts#
The Debug and Validation tools offer several key functionalities:
- Tile Inspection: The ability to click on an individual tile on the map and view detailed information about it.
- Property Display: Showing technical metadata for a selected tile, such as its ID, depth in the tileset tree, number of vertices, refinement strategy, distance from camera, and whether it has textures or materials.
- Visual Debugging Overlays: Rendering additional visual elements directly on the 3D tiles to help diagnose issues, such as:
- Wireframe: Showing the triangle mesh of the geometry.
- Bounding Volumes: Displaying the boxes or spheres that define the spatial extent of tiles, which helps understand the tileset's spatial partitioning.
- Normals: Visualizing the surface normals of the geometry, important for lighting and shading.
- Texture UVs: Overlaying a special texture to check how textures are mapped onto the geometry.
- Tile Coloring: Coloring tiles based on different properties (like depth, distance from camera, or a custom color) to understand the tileset structure and behavior.
- Automated Validation Checks: Running specific tests on a selected tile to identify common data quality issues, such as geometry extending outside its defined bounding volume, problematic LOD (Level of Detail) settings, or inconsistencies between geometry and texture coordinates.
How to Use#
These tools are primarily accessed through the Debug Panel and the Tile Details Panel.
1. Accessing the Debug Panel#
The main gateway to many debugging visualizations is the Debug Panel.
- In the application's main interface (specifically on the Debug page/mode), find the main tools panel (either on the right side on desktop or bottom on mobile, handled by
MainToolsPanel
orMobileToolsPanel
components inDebugApp.tsx
). - Click the "Debug" button (often has a bug icon).
This will open the DebugPanel
component (src/components/debug-panel/debug-panel.tsx
). This panel contains various toggle switches and radio buttons to enable or disable visual debugging overlays and change tile coloring modes.
// Simplified snippet from src/components/debug-panel/debug-panel.tsx
export const DebugPanel = ({ onClose }: DebugPanelProps) => {
const dispatch = useAppDispatch();
const debugOptions = useAppSelector(selectDebugOptions); // Get debug settings from state
return (
<PanelContainer> {/* ... panel structure ... */}
<PanelHeader> {/* ... header ... */}</PanelHeader>
<CloseButton onClick={onClose} />
<PanelHorizontalLine />
<ToggleOptionsContainer>
<ItemContainer>
<Title>Wireframe mode</Title>
<ToggleSwitch // Example Toggle Switch
checked={debugOptions.wireframe} // Reads state
onChange={() => // Dispatches action on change
dispatch(setDebugOptions({ wireframe: !debugOptions.wireframe }))
}
/>
</ItemContainer>
{/* ... other toggle switches ... */}
<Title>Color</Title>
<RadioButtonWrapper>
{Object.keys(TileColoredBy).map((value) => {
const selected = debugOptions.tileColorMode === TileColoredBy[value];
return (
<ListItem // Example Radio Button (ListItem with type=Radio)
key={value}
title={TileColoredBy[value]}
type={ListItemType.Radio}
selected={selected} // Reads state
onChange={() => // Dispatches action on change
dispatch(setDebugOptions({ tileColorMode: TileColoredBy[value] }))
}
/>
);
})}
</RadioButtonWrapper>
{/* ... other radio button groups and options ... */}
</ToggleOptionsContainer>
{/* ... upload texture panel ... */}
</PanelContainer>
);
};
This snippet shows how the DebugPanel
component uses ToggleSwitch
and ListItem
components to display and control various debug options. Each control reads its current state (checked
or selected
) from the debugOptions
in the Redux store (Chapter 4: Global State Management) using useAppSelector(selectDebugOptions)
. When a control is toggled or selected, its onChange
handler dispatches an action (setDebugOptions
) to update that specific option in the global state. The Map Visualization component (DeckGlWrapper
) reads these debugOptions
from the state and applies the corresponding visual overlays or coloring.
2. Toggling Visual Overlays and Tile Coloring#
In the Debug Panel:
- Flip the toggle switches next to options like "Wireframe mode", "Bounding Volumes", "Texture UVs", or "Show normals".
- Under the "Color" section, select a radio button for options like "By Depth", "By LOD", or "By distance to camera". If "Custom" is selected, a color picker will appear in the Tile Details Panel (see below).
As soon as you change these settings, the Map Visualization will update to show the selected overlays or coloring.
3. Accessing the Tile Details Panel#
To inspect a specific tile:
- Ensure "Enable picking" is toggled on in the Debug Panel.
- Click directly on a tile visible on the map.
If picking is enabled and you click a tile, a Tile Details Panel (src/components/tile-details-panel/tile-details-panel.tsx
) will appear, typically near the clicked tile or in a fixed location. This panel shows detailed information about the selected tile.
// Simplified snippet from src/pages/debug-app/debug-app.tsx's handleClick function
const handleClick = (info: PickingInfo) => {
// info.object contains the Tile3D object that was clicked
if (!info.object) {
// If nothing was clicked (or picking is off/failed)
handleCloseTilePanel(); // Hide the panel
return;
}
// If a tile was clicked, set it as the selected tile in the component's state
setSelectedTile(info.object);
// Also reset any previously generated normals debug data for a different tile
setNormalsDebugData(null);
};
This handleClick
function, part of the DebugApp
page component, is called by the MapWrapper
component (DeckGlWrapper
) whenever the user clicks on the map and picking is enabled. If the click hit a tile (info.object
is a Tile3D
object), it updates the selectedTile
state variable in DebugApp
. This state change causes the DebugApp
component to re-render, and if selectedTile
is not null, it renders the TileDetailsPanel
component, passing the selectedTile
object to it.
4. Inspecting Tile Properties#
When the Tile Details Panel is open, you'll see various properties listed, such as:
- Tile ID
- Type (e.g., 'mesh')
- Children Count and IDs
- Vertex Count
- Distance to Camera
- Refinement Type (Add/Replace)
- Has Texture / Has Material
- Bounding Type (Sphere, Box, Region)
- LOD Metric Type/Value
- Screen Space Error
- Depth in the tree
This information is displayed using the TileMetadata
component within TileDetailsPanel.tsx
, which simply formats and presents the properties from the selected Tile3D
object.
// Simplified snippet showing how tile properties are accessed in TileDetailsPanel.tsx
export const TileDetailsPanel = ({ tile, /* ... other props ... */ }) => {
// Access properties directly from the Tile3D object passed as a prop
const {
id,
type,
header: { children: tileChildren }, // Nested access
distanceToCamera,
content: { vertexCount, texture, material }, // Access content properties
refine,
lodMetricType,
lodMetricValue,
screenSpaceError,
depth,
} = tile; // Destructure properties from the tile object
// Prepare an array of objects for display in the TileMetadata component
const TILE_INFO: TileInfo[] = [
{ title: "Tile Id", value: formatStringValue(id) || NO_DATA },
{ title: "Type", value: formatStringValue(type) || NO_DATA },
// ... other properties mapped similarly ...
{ title: "Vertex count", value: formatIntValue(vertexCount) || NO_DATA },
{ title: "Has Texture", value: Boolean(texture).toString() },
// ... etc. ...
];
return (
<Container> {/* ... */}
{/* ... Header, Back Button, Close Button ... */}
<ContentWrapper>
{/* ... Validate Button ... */}
<TileMetadata tileInfo={TILE_INFO} /> {/* Display the properties */}
{/* ... TextureSection, Normals ... */}
</ContentWrapper>
</Container>
);
};
This demonstrates how the TileDetailsPanel
receives the tile
object as a prop and directly accesses its properties (id
, type
, content.vertexCount
, etc.) to build the list of information displayed by the TileMetadata
component. Helper functions (formatStringValue
, formatIntValue
) are used to format the values nicely for display.
5. Visualizing Normals and Texture UVs (Specific Overlays)#
The Normals
section in the Tile Details Panel allows fine-grained control over visualizing normals for the selected tile:
- Toggle "Show normals". This will draw lines representing normals on the tile geometry.
- Use the input fields to change the "Percent of triangles with normals" (useful for dense meshes) and "Normals length".
This is handled by the Normals
component (src/components/tile-details-panel/normals.tsx
), which takes handlers (onShowNormals
, onChangeTrianglesPercentage
, onChangeNormalsLength
) as props. These handlers update the normalsDebugData
, trianglesPercentage
, and normalsLength
state variables in the parent DebugApp
component. The MapWrapper
component in DebugApp
receives these updated values as props and uses them to render the normal lines via a dedicated layer (often a LineLayer
). The actual data generation for the normal lines is done by the generateBinaryNormalsDebugData
utility function (src/utils/debug/normals-utils.ts
) based on the tile's geometry and normal attributes.
Similarly, toggling "Texture UVs" in the main Debug Panel (src/components/debug-panel/debug-panel.tsx
) enables an overlay using a specific debug texture. This is controlled by the showUVDebugTexture
debug option in the Redux state. When this option is true, the DeckGlWrapper
uses a special shader and texture provided via the loadDebugTextureImage
prop to visualize the UV mapping. The Debug Panel also allows uploading custom UV debug textures via a file upload modal (UploadPanel
).
6. Custom Tile Coloring#
If you select "Custom" under the "Color" options in the Debug Panel, the Tile Details Panel for the selected tile will show a color picker (TileColorSection
).
- Use the color picker to choose a specific color. The selected tile will change to this color on the map.
- Use the "Reset Color" button to remove the custom color for that tile.
This functionality allows you to highlight specific tiles of interest with a custom color. The selected color for each tile ID is stored in the coloredTilesMap
state variable within DebugApp.tsx
. This map is passed down to the MapWrapper
component via the coloredTilesMap
prop, which uses it to apply the custom colors instead of the default coloring mode (like coloring by depth).
// Simplified snippet showing TileColorSection in TileDetailsPanel.tsx
export const TileDetailsPanel = ({ tile, /* ... other props ... */ }) => {
// ...
return (
<Container> {/* ... */}
{/* ... other sections ... */}
{/* Conditionally show color picker if custom tile coloring is enabled */}
{isShowColorPicker && (
<TileColorSection // Component for the color picker and reset button
tileId={tileId}
tileSelectedColor={tileSelectedColor} // Current color for this tile
isResetButtonDisabled={isResetButtonDisabled} // Is Reset button enabled?
handleResetColor={handleResetColor} // Handler for Reset button
handleSelectTileColor={handleSelectTileColor} // Handler when a color is picked
/>
)}
{/* ... Normals section ... */}
</Container>
);
};
The TileColorSection
component receives the tileId
and the current color for that tile from its parent (TileDetailsPanel
, which gets it from DebugApp
's coloredTilesMap
). When the user interacts with the color picker or reset button, the provided handlers (handleSelectTileColor
, handleResetColor
) are called, which update the coloredTilesMap
state in DebugApp
, triggering a re-render of the map with the new custom tile color.
7. Using the Validator Panel#
The Tile Details Panel also provides access to the automated validation checks:
- In the Tile Details Panel, click the "Validate Tile" button.
This action triggers a series of validation checks on the selected tile. The Tile Details Panel then switches views to display the Validator Panel (src/components/semantic-validator/semantic-validator.tsx
), showing a list of issues found (Warnings) and checks that passed (OK).
// Simplified snippet showing the Validate Button and panel switching in TileDetailsPanel.tsx
export const TileDetailsPanel = ({ tile, /* ... other props ... */ }) => {
// State to manage which panel is active (Details or Validator)
const [activeTileInfoPanel, setActiveTileInfoPanel] =
useState<ActiveTileInfoPanel>(ActiveTileInfoPanel.TileDetailsPanel);
// State to store validation results
const [validateTileWarnings, setValidateTileWarnings] = useState([]);
const [validateTileOk, setValidateTileOk] = useState([]);
// Function called when "Validate Tile" button is clicked
const onValidateTile = (tile: Tile3D) => {
// Call validation utility functions
validateGeometryInsideBoundingVolume(tile);
validateGeometryVsTexture(tile);
compareGeometryBoundingVolumeVsTileBoundingVolume(tile);
// Switch to the Validator Panel view
setActiveTileInfoPanel(ActiveTileInfoPanel.ValidateTilePanel);
};
// ... useEffect to reset state when tile changes ...
const isDetailsPanel = activeTileInfoPanel === ActiveTileInfoPanel.TileDetailsPanel;
return (
<Container> {/* ... */}
{/* ... Header with Back/Close buttons ... */}
<ContentWrapper>
{/* Show Validate Button only in Details view */}
{isDetailsPanel && (
<ValidateButton
onClick={() => { onValidateTile(tile); }} // Calls onValidateTile handler
>
<ValidatorTitle>{VALIDATE_TILE}</ValidatorTitle>
<ArrowContainer> <ChevronIcon /> </ArrowContainer>
</ValidateButton>
/* ... TileMetadata, Texture, Normals sections ... */
)}
{/* Show Validator Panel if not in Details view */}
{!isDetailsPanel && (
<ValidateTilePanel // Pass validation results as props
validatedTileWarnings={validateTileWarnings}
validatedTileOk={validateTileOk}
/>
)}
</ContentWrapper>
</Container>
);
};
When you click "Validate Tile", the onValidateTile
function is called. This function internally calls utility functions (like validateGeometryInsideBoundingVolume
, which uses isTileGeometryInsideBoundingVolume
from src/utils/debug/tile-debug.ts
) that perform the actual checks on the tile
object. These utility functions (or helpers they call) return boolean results or error messages, which the onValidateTile
function then uses to update the validateTileWarnings
and validateTileOk
state variables. Finally, it updates the activeTileInfoPanel
state, which causes the TileDetailsPanel
component to hide the tile details sections and render the ValidateTilePanel
instead, passing it the collected warnings and successes.
The SemanticValidator
component (src/components/semantic-validator/semantic-validator.tsx
) simply receives the warnings
and validatedTileOk
arrays as props and renders them, usually in a table format, displaying the type and description of each issue or success. It also provides a "Clear" button to empty the list.
// Simplified snippet from src/components/semantic-validator/semantic-validator.tsx
export const SemanticValidator = ({ warnings = [], clearWarnings, onClose }: SemanticValidatorProps) => {
return (
<PanelContainer> {/* ... */}
<PanelHeader> {/* ... header with Close button ... */}</PanelHeader>
<PanelHorizontalLine />
<PanelContent>
{/* Conditional rendering based on whether there are warnings */}
{warnings && Boolean(warnings.length)
? (
<Table>
<thead> {/* ... table header ... */}
<TableRow>
{/* ... header columns ... */}
<TableHeader>Warning <TableButton onClick={clearWarnings}>Clear</TableButton></TableHeader>
</TableRow>
</thead>
<tbody>
{/* Map through warnings and render a table row for each */}
{warnings.map((warning, index) => (
<tr key={`${warning.title}-${index}`}>
<td>{index + 1}</td> {/* Row number */}
<td>{WARNING_TYPES[warning.type]}</td> {/* Warning Type */}
<td>{warning.title}</td> {/* Warning description */}
</tr>
))}
</tbody>
</Table>
)
: (
<NoIssuesItem>{NO_ISSUES}</NoIssuesItem> // Message if no warnings
)}
</PanelContent>
</PanelContainer>
);
};
This shows how SemanticValidator
receives the warnings
array. If the array is not empty, it renders a table header and then maps over the warnings
array, creating a table row (<tr>
) for each warning and displaying its details. If the warnings
array is empty, it displays the "No Issues" message. The clearWarnings
prop is called when the "Clear" button is clicked, typically resetting the warnings state in the parent component (TileDetailsPanel
which gets it from DebugApp
).
8. Performance and Memory Statistics#
While not strictly "validation" of data content, the application also provides panels to view runtime performance metrics, which are valuable debugging tools.
- In the main tools panel, click the "Memory" button (often shows memory usage).
This opens the MemoryUsagePanel
(src/components/memory-usage-panel/memory-usage-panel.tsx
). This panel displays statistics like WebGL memory usage (obtained via lumaStats
), tileset loading times, and loaded tile counts. These statistics help identify performance bottlenecks or excessive memory consumption. The MemoryUsagePanel
reads these statistics from the tilesetsStats
and memoryStats
state variables managed in the DebugApp
component.
Under the Hood: The Debugging Workflow#
Here's a simplified flow of how interacting with the Debug and Validation tools works:
sequenceDiagram
Participant User
Participant MainToolsPanel
Participant DebugPanelUI
Participant ReduxState
Participant MapWrapper
Participant TileDetailsPanelUI
Participant SemanticValidatorUI
Participant DebugUtils
User->>MainToolsPanel: Clicks "Debug" button
MainToolsPanel->>DebugPanelUI: Opens Debug Panel
User->>DebugPanelUI: Toggles "Wireframe" switch
DebugPanelUI->>ReduxState: Dispatches setDebugOptions({wireframe: true})
ReduxState-->>MapWrapper: State change notifies map
MapWrapper->>User: Map renders with wireframe overlay
User->>User: Pans/Zooms map
User->>MapWrapper: Clicks on a tile
MapWrapper->>DebugApp: Calls handleClick with Tile3D object
DebugApp->>DebugApp: Sets selectedTile state
DebugApp-->>TileDetailsPanelUI: Renders Tile Details Panel for selected tile
TileDetailsPanelUI->>User: Shows tile properties
User->>TileDetailsPanelUI: Clicks "Validate Tile" button
TileDetailsPanelUI->>DebugUtils: Calls validation utilities (e.g., validateGeometryInsideBoundingVolume)
DebugUtils->>DebugUtils: Checks tile data properties
DebugUtils-->>TileDetailsPanelUI: Returns validation results (warnings/ok)
TileDetailsPanelUI->>TileDetailsPanelUI: Updates state with validation results, switches view
TileDetailsPanelUI-->>SemanticValidatorUI: Renders Semantic Validator Panel with results
SemanticValidatorUI->>User: Shows list of warnings/successes
- Accessing Tools: User clicks buttons in the
MainToolsPanel
to open theDebugPanel
,BookmarksPanel
,MemoryUsagePanel
, etc. TheMainToolsPanel
updates theactiveButton
state inDebugApp
, which conditionally renders the appropriate panel component (DebugPanel
,SemanticValidator
,MemoryUsagePanel
). - Debug Options: Interacting with controls in
DebugPanel
dispatches actions (setDebugOptions
) to update thedebugOptions
state in Redux (debug-options-slice.ts
). TheMapWrapper
component reads these options from Redux viauseAppSelector(selectDebugOptions)
and applies the visual changes (wireframe, coloring, etc.). Custom UV textures are handled via an upload flow inDebugPanel
that results in a texture being available to the MapWrapper. - Tile Picking: Clicking the map when picking is enabled triggers the
onClick
handler inDeckGlWrapper
orArcgisWrapper
. This handler identifies the clicked object (if it's a tile) and calls thehandleClick
function provided by the parentDebugApp
.handleClick
sets theselectedTile
state, causingDebugApp
to render theTileDetailsPanel
, passing the selectedTile3D
object. - Tile Details:
TileDetailsPanel
accesses properties directly from thetile
prop to display metadata (TileMetadata
), texture info (TextureSection
), and normals controls (Normals
). Interacting with Normals or the custom color picker (TileColorSection
) triggers handlers that update state variables inDebugApp
(normalsDebugData
,coloredTilesMap
), which are then passed as props to theMapWrapper
to update the visualization. - Validation: Clicking "Validate Tile" in
TileDetailsPanel
calls utility functions (validateGeometryInsideBoundingVolume
, etc.) defined in files likesrc/utils/debug/tile-debug.ts
andsrc/utils/debug/validation-utils
. These functions analyze the tile data and return results. TheTileDetailsPanel
updates its internal state with these results and switches to rendering theSemanticValidator
component, passing it the results to display. - Memory Stats: The
MemoryUsagePanel
component readstilesetsStats
(compiled byDebugApp
usingsumTilesetsStats
andonAfterRender
hook fromMapWrapper
) andmemoryStats
(obtained vialumaStats
ononWebGLInitialized
) from the state managed inDebugApp
and displays them.
This interconnected system allows users to go from a high-level visual issue on the map to inspecting specific tile properties, enabling detailed visual overlays, and running automated checks to pinpoint data quality problems.
Conclusion#
In this chapter, we explored the Debug and Validation Tools available in loaders.gl-showcases
. We learned how the Debug Panel provides control over global visual overlays like wireframe and tile coloring, interacting with the Redux state to update the Map Visualization. We saw how clicking on a tile opens the Tile Details Panel to display its properties and provide tools for specific tile debugging, such as visualizing normals or applying custom colors. Finally, we looked at the Validator Panel, accessible from the Tile Details Panel, which runs automated checks on tile data quality based on utility functions, helping users identify common issues.
These tools empower users and developers to look "under the hood" of the 3D data, diagnose visual problems, understand tileset structure and performance, and assess data quality β essential capabilities when working with complex 3D geospatial content.
This concludes our tutorial on loaders.gl-showcases
. We've journeyed from understanding the basic User Interface Components and the Map Visualization stage to managing Layers, utilizing Global State Management, and exploring powerful features like Bookmarks, Comparison Mode, ArcGIS Integration, and the Debug and Validation Tools covered in this chapter. We hope this tutorial has provided you with a solid foundation for understanding and using the application.
Generated by AI Codebase Knowledge Builder. References: 1(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/components/debug-panel/debug-panel.tsx), 2(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/components/semantic-validator/semantic-validator.tsx), 3(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/components/tile-details-panel/normals.tsx), 4(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/components/tile-details-panel/tile-details-panel.tsx), 5(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/pages/debug-app/debug-app.tsx), 6(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/utils/debug/normals-utils.ts), 7(https://github.com/visgl/loaders.gl-showcases/blob/3403a56b6839455092211a95c5cd695f20ea6c7e/src/utils/debug/tile-debug.ts)