- Qt 6.4
- Qt Data Visualization
- Qt Quick 2 Surface Multiseries Example
- Qt Data Visualization | Commercial or GPLv3

# Qt Quick 2 Surface Multiseries Example

Using multiple series with Surface3D in a QML application.

The Qt Quick 2 surface example shows how to make a 3D surface plot displaying 3 layers using Surface3D with Qt Quick 2.

The focus in this example is on generating a multiseries surface plot from 3 different height map images, so in this section we skip explaining the application creation. For a more detailed QML example documentation, see Qt Quick 2 Scatter Example.

#### Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

#### Adding Data to the Graph

This example shows how to add several surface series to one graph using using HeightMapSurfaceDataProxies and how to control their visibilities individually.

Let's start by creating a specific gradient for each layer:

     ColorGradient {
         id: layerOneGradient
         ColorGradientStop { position: 0.0; color: "black" }
         ColorGradientStop { position: 0.31; color: "tan" }
         ColorGradientStop { position: 0.32; color: "green" }
         ColorGradientStop { position: 0.40; color: "darkslategray" }
         ColorGradientStop { position: 1.0; color: "white" }
     }

     ColorGradient {
         id: layerTwoGradient
         ColorGradientStop { position: 0.315; color: "blue" }
         ColorGradientStop { position: 0.33; color: "white" }
     }

     ColorGradient {
         id: layerThreeGradient
         ColorGradientStop { position: 0.0; color: "red" }
         ColorGradientStop { position: 0.15; color: "black" }
     }

Then we'll create the series themselves. It happens simply by adding 3 separate Surface3DSeries to the Surface3D graph as children:

     ...
     Surface3DSeries {
         id: layerOneSeries
         baseGradient: layerOneGradient
         HeightMapSurfaceDataProxy {
             heightMapFile: ":/heightmaps/layer_1.png"
         }
         flatShadingEnabled: false
         drawMode: Surface3DSeries.DrawSurface
         visible: layerOneToggle.checked // bind to checkbox state
     }

     Surface3DSeries {
         id: layerTwoSeries
         baseGradient: layerTwoGradient
         HeightMapSurfaceDataProxy {
             heightMapFile: ":/heightmaps/layer_2.png"
         }
         flatShadingEnabled: false
         drawMode: Surface3DSeries.DrawSurface
         visible: layerTwoToggle.checked // bind to checkbox state
     }

     Surface3DSeries {
         id: layerThreeSeries
         baseGradient: layerThreeGradient
         HeightMapSurfaceDataProxy {
             heightMapFile: ":/heightmaps/layer_3.png"
         }
         flatShadingEnabled: false
         drawMode: Surface3DSeries.DrawSurface
         visible: layerThreeToggle.checked // bind to checkbox state
     }
     ...

You'll notice we added the created gradients to the `baseGradient` properties of the series. We could have added them to the `baseGradients` property of the Theme3D in Surface3D instead, but doing it this way ensures each gradient is applied to a correct series:

     Surface3DSeries {
         id: layerOneSeries
         baseGradient: layerOneGradient
         ...

#### Controlling the Graph

Let's add some checkboxes to control the visibility of layers:

     GroupBox {
         Layout.fillWidth: true
         Column {
             spacing: 10

             Label {
                 font.pointSize: fontSize
                 font.bold: true
                 text: portraitMode ? "Layer\nSelection" : "Layer Selection"
             }

             CheckBox {
                 id: layerOneToggle
                 checked: true
                 text: portraitMode ? "Show\nGround\nLayer" : "Show Ground Layer"
             }

             CheckBox {
                 id: layerTwoToggle
                 checked: true
                 text: portraitMode ? "Show\nSea\nLayer" : "Show Sea Layer"
             }

             CheckBox {
                 id: layerThreeToggle
                 checked: true
                 text: portraitMode ? "Show\nTectonic\nLayer" : "Show Tectonic Layer"
             }
         }
     }

We don't need to do anything on the `onCheckedChanged` as we bound the `checked` state to the `visible` property of the series directly:

     ...
     visible: layerOneToggle.checked // bind to checkbox state
     ...

Let's add some more checkboxes to control how the layers are displayed, when visible:

     GroupBox {
         Layout.fillWidth: true
         Column {
             spacing: 10

             Label {
                 font.pointSize: fontSize
                 font.bold: true
                 text: portraitMode ? "Layer\nStyle" : "Layer Style"
             }

             CheckBox {
                 id: layerOneGrid
                 text: portraitMode ? "Show\nGround\nas Grid" : "Show Ground as Grid"
                 onCheckedChanged: {
                     if (checked)
                         layerOneSeries.drawMode = Surface3DSeries.DrawWireframe
                     else
                         layerOneSeries.drawMode = Surface3DSeries.DrawSurface
                 }
             }

             CheckBox {
                 id: layerTwoGrid
                 text: portraitMode ? "Show\nSea\nas Grid" : "Show Sea as Grid"

                 onCheckedChanged: {
                     if (checked)
                         layerTwoSeries.drawMode = Surface3DSeries.DrawWireframe
                     else
                         layerTwoSeries.drawMode = Surface3DSeries.DrawSurface
                 }
             }

             CheckBox {
                 id: layerThreeGrid
                 text: portraitMode ? "Show\nTectonic\nas Grid" : "Show Tectonic as Grid"
                 onCheckedChanged: {
                     if (checked)
                         layerThreeSeries.drawMode = Surface3DSeries.DrawWireframe
                     else
                         layerThreeSeries.drawMode = Surface3DSeries.DrawSurface
                 }
             }
         }
     }

In addition to these we have three buttons, one of which is of special interest to us. It is used to control whether we want to slice into only one layer, or all of them:

     Button {
         id: sliceButton
         text: portraitMode ? "Slice\nAll\nLayers" : "Slice All Layers"
         Layout.fillWidth: true
         Layout.minimumHeight: 40
         onClicked: {
             if (surfaceLayers.selectionMode & AbstractGraph3D.SelectionMultiSeries) {
                 surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                         | AbstractGraph3D.SelectionSlice
                 text = portraitMode ? "Slice\nAll\nLayers" : "Slice All Layers"
             } else {
                 surfaceLayers.selectionMode = AbstractGraph3D.SelectionRow
                         | AbstractGraph3D.SelectionSlice
                         | AbstractGraph3D.SelectionMultiSeries
                 text = portraitMode ? "Slice\nOne\nLayer" : "Slice One Layer"
             }
         }
     }

#### Example Contents

Example project @ code.qt.io