|
| 1 | +The goal of this tutorial is to learn how to visualize 3D tiles textured mesh data (in the b3dm format). |
| 2 | +We will use a [data set](https://github.com/iTowns/iTowns2-sample-data/tree/master/3DTiles/lyon_1_3946_textured_draco) representing buildings of the 1st borrough of Lyon. |
| 3 | +The original data are openly available on [Lyon metropolis open data](https://data.grandlyon.com/) and have been transformed in 3D tiles with [py3dtilers](https://github.com/VCityTeam/py3dtilers/). |
| 4 | + |
| 5 | +## Preparing the webpage |
| 6 | + |
| 7 | +The webpage we want to display data on should be structured as follows : |
| 8 | +```html |
| 9 | +<!DOCTYPE html> |
| 10 | +<html> |
| 11 | + <head> |
| 12 | + <meta charset="UTF-8"> |
| 13 | + <title>Display 3D Tiles b3dm dataset with iTowns</title> |
| 14 | + <style> |
| 15 | + html { height: 100%; } |
| 16 | + body { margin: 0; overflow: hidden; height: 100%; } |
| 17 | + #viewerDiv { margin: auto; height: 100%; width: 100%; padding: 0; } |
| 18 | + canvas { display: block } |
| 19 | + </style> |
| 20 | + </head> |
| 21 | + <body> |
| 22 | + <div id="viewerDiv"></div> |
| 23 | + <script src="../dist/itowns.js"></script> |
| 24 | + <script type="text/javascript"> |
| 25 | + // Tutorial code should go here |
| 26 | + </script> |
| 27 | + </body> |
| 28 | +</html> |
| 29 | +``` |
| 30 | + |
| 31 | +To work as is, this web page should be placed in the `examples/` folder of [itowns](https://github.com/iTowns/itowns) but you can put it anywhere else as |
| 32 | +long as you have a local web server and that you adapt the link to itowns (`<script src="../dist/itowns.js"></script>`). |
| 33 | + |
| 34 | + |
| 35 | +## Preparing the field |
| 36 | + |
| 37 | +We will first create a view, add a layer with ortho images and a digital elevation model (DEM). |
| 38 | +The 3D Tiles dataset we are using is in the `EPSG:3946` CRS, so we will use a `{@link PlanarView}` in this CRS to display it. |
| 39 | + |
| 40 | +We won't go into the details of creating the view, adding the ortho images and the DEM. For more information on this part, see the [Raster visualization in Lambert Conformical conic projection]{@tutorial Raster-data-Lambert93} in which we do the same but in the `EPSG:2154` projection. You can use the following code that prepared the field with such data: |
| 41 | + |
| 42 | +```js |
| 43 | + // Define crs projection that we will use (taken from https://epsg.io/3946, Proj4js section) |
| 44 | + itowns.proj4.defs('EPSG:3946', |
| 45 | + '+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80' + |
| 46 | + '+towgs84=0,0,0,0,0,0,0 +units=m +no_defs'); |
| 47 | + |
| 48 | + // Define geographic extent: CRS, min/max X, min/max Y |
| 49 | + var extent = new itowns.Extent( 'EPSG:3946', |
| 50 | + 1837816.94334, 1847692.32501, |
| 51 | + 5170036.4587, 5178412.82698); |
| 52 | + |
| 53 | + // `viewerDiv` will contain iTowns' rendering area (`<canvas>`) |
| 54 | + var viewerDiv = document.getElementById('viewerDiv'); |
| 55 | + |
| 56 | + // Instanciate PlanarView* |
| 57 | + var cameraCoord = new itowns.Coordinates('EPSG:3946', 1841980, |
| 58 | + 5175682, 3000) |
| 59 | + var view = new itowns.PlanarView(viewerDiv, extent, { placement: { |
| 60 | + coord: cameraCoord, heading: 30, range: 4000, tilt: 30 } }); |
| 61 | + |
| 62 | + // Add a WMS imagery source |
| 63 | + var wmsImagerySource = new itowns.WMSSource({ |
| 64 | + extent: extent, |
| 65 | + name: 'Ortho2009_vue_ensemble_16cm_CC46', |
| 66 | + url: 'https://download.data.grandlyon.com/wms/grandlyon', |
| 67 | + version: '1.3.0', |
| 68 | + crs: 'EPSG:3946', |
| 69 | + format: 'image/jpeg', |
| 70 | + }); |
| 71 | + |
| 72 | + // Add a WMS imagery layer |
| 73 | + var wmsImageryLayer = new itowns.ColorLayer('wms_imagery', { |
| 74 | + updateStrategy: { |
| 75 | + type: itowns.STRATEGY_DICHOTOMY, |
| 76 | + options: {}, |
| 77 | + }, |
| 78 | + source: wmsImagerySource, |
| 79 | + }); |
| 80 | + |
| 81 | + view.addLayer(wmsImageryLayer); |
| 82 | + |
| 83 | + // Add a WMS elevation source |
| 84 | + var wmsElevationSource = new itowns.WMSSource({ |
| 85 | + extent: extent, |
| 86 | + url: 'https://download.data.grandlyon.com/wms/grandlyon', |
| 87 | + name: 'MNT2012_Altitude_10m_CC46', |
| 88 | + crs: 'EPSG:3946', |
| 89 | + width: 256, |
| 90 | + format: 'image/jpeg', |
| 91 | + }); |
| 92 | + |
| 93 | + // Add a WMS elevation layer |
| 94 | + var wmsElevationLayer = new itowns.ElevationLayer('wms_elevation', { |
| 95 | + useColorTextureElevation: true, |
| 96 | + colorTextureElevationMinZ: 144, |
| 97 | + colorTextureElevationMaxZ: 622, |
| 98 | + source: wmsElevationSource, |
| 99 | + }); |
| 100 | + |
| 101 | + view.addLayer(wmsElevationLayer); |
| 102 | +``` |
| 103 | + |
| 104 | +## Adding the 3D Tiles Layer |
| 105 | + |
| 106 | +The 3D Tiles dataset we are using in this example has a geometry that is compressed with |
| 107 | +[Draco compression](https://google.github.io/draco/) for better performances. Therefore, we need to enable the draco |
| 108 | +decoder: |
| 109 | + |
| 110 | +```js |
| 111 | +itowns.enableDracoLoader('./libs/draco/'); |
| 112 | +``` |
| 113 | + |
| 114 | +As for every data displayed in iTowns, we first need to define a data `Source`. |
| 115 | +Our data is in the 3d-tiles format, so we can use iTowns `{@link C3DTilesSource}` : |
| 116 | + |
| 117 | +```js |
| 118 | +const buildingsSource = new itowns.C3DTilesSource({ |
| 119 | + url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/' + |
| 120 | + '3DTiles/lyon_1_3946_textured_draco/tileset.json', |
| 121 | +}); |
| 122 | +``` |
| 123 | + |
| 124 | +It is worth noting that the 3d-tiles data we want to display on a given `{@link View}` must be in the same Coordinates |
| 125 | +Reference System (CRS) as the `{@link View}`. |
| 126 | +Here, our 3d-tiles data are in [RGF93 / CC46](https://epsg.io/3946) projection, just like our `{@link PlanarView}`. |
| 127 | +This is the reason why we do not need to specify a `crs` parameter when instantiating our `{@link C3DTilesSource}`. |
| 128 | + |
| 129 | +Now that the source of our data is set, we need to create a `{@link Layer}` which will contain the data. |
| 130 | +To display 3d-tiles data, iTowns comes with a `{@link C3DTilesLayer}`, which we can use as such : |
| 131 | + |
| 132 | +```js |
| 133 | +const buildingsLayer = new itowns.C3DTilesLayer('buildings', { |
| 134 | + source: buildingsSource, |
| 135 | +}, view); |
| 136 | +itowns.View.prototype.addLayer.call(view, buildingsLayer); |
| 137 | +``` |
| 138 | + |
| 139 | +When instantiating a `{@link C3DTilesLayer}`, we need to specify which `{@link View}` it is added to. |
| 140 | +We also need to call the generic `addLayer` method from `{@link View}`, and not the specific one from |
| 141 | +`{@link PlanarView}`. |
| 142 | +This is because both 3d-tiles data and `{@link PlanarView}` have their own spatial subdivision. |
| 143 | +Therefore, 3d-tiles data must not use specific `{@link PlanarView}` spatial subdivision (which is by default the case when using the `addLayer` method of `PlanarView` or `GlobeView`). |
| 144 | + |
| 145 | +The code above results in the following : |
| 146 | + |
| 147 | + |
| 148 | + |
| 149 | +We can see our buildings, but they are all black. |
| 150 | +In order to improve their visualisation, we can add light effects to our view. |
| 151 | + |
| 152 | +## Add light effects |
| 153 | + |
| 154 | +We can use ThreeJS [`DirectionalLight`](https://threejs.org/docs/index.html#api/en/lights/DirectionalLight) and |
| 155 | +[`AmbientLight`](https://threejs.org/docs/index.html#api/en/lights/AmbientLight) to add light effects to our view. |
| 156 | +We just need to implement them as we would in any [ThreeJS](https://threejs.org/) application : |
| 157 | + |
| 158 | +```js |
| 159 | +const directionalLight = new itowns.THREE.DirectionalLight(0xffffff, 1); |
| 160 | +directionalLight.position.set(-0.9, 0.3, 1); |
| 161 | +directionalLight.updateMatrixWorld(); |
| 162 | +view.scene.add(directionalLight); |
| 163 | + |
| 164 | +const ambientLight = new itowns.THREE.AmbientLight(0xffffff, 1); |
| 165 | +view.scene.add(ambientLight); |
| 166 | +``` |
| 167 | + |
| 168 | +We can now see our buildings with some light effects : |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | +## Result |
| 173 | + |
| 174 | +By reaching here, you are now able to display some mesh data in 3d-tiles format. |
| 175 | +The final code to do so is the following : |
| 176 | + |
| 177 | +```html |
| 178 | +<!DOCTYPE html> |
| 179 | +<html> |
| 180 | + <head> |
| 181 | + <meta charset="UTF-8"> |
| 182 | + <title>Display 3D Tiles b3dm dataset with iTowns</title> |
| 183 | + <style> |
| 184 | + html { height: 100%; } |
| 185 | + body { margin: 0; overflow: hidden; height: 100%; } |
| 186 | + #viewerDiv { margin: auto; height: 100%; width: 100%; padding: 0; } |
| 187 | + canvas { display: block } |
| 188 | + </style> |
| 189 | + </head> |
| 190 | + <body> |
| 191 | + <div id="viewerDiv"></div> |
| 192 | + <script src="../dist/itowns.js"></script> |
| 193 | + <script type="text/javascript"> |
| 194 | + // Define crs projection that we will use (taken from https://epsg.io/3946, Proj4js section) |
| 195 | + itowns.proj4.defs('EPSG:3946', |
| 196 | + '+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80' + |
| 197 | + '+towgs84=0,0,0,0,0,0,0 +units=m +no_defs'); |
| 198 | +
|
| 199 | + // Define geographic extent: CRS, min/max X, min/max Y |
| 200 | + var extent = new itowns.Extent( 'EPSG:3946', |
| 201 | + 1837816.94334, 1847692.32501, |
| 202 | + 5170036.4587, 5178412.82698); |
| 203 | +
|
| 204 | + // `viewerDiv` will contain iTowns' rendering area (`<canvas>`) |
| 205 | + var viewerDiv = document.getElementById('viewerDiv'); |
| 206 | +
|
| 207 | + // Instanciate PlanarView* |
| 208 | + var cameraCoord = new itowns.Coordinates('EPSG:3946', 1841980, |
| 209 | + 5175682, 3000) |
| 210 | + var view = new itowns.PlanarView(viewerDiv, extent, { placement: { |
| 211 | + coord: cameraCoord, heading: 30, range: 4000, tilt: 30 } }); |
| 212 | +
|
| 213 | + // Add a WMS imagery source |
| 214 | + var wmsImagerySource = new itowns.WMSSource({ |
| 215 | + extent: extent, |
| 216 | + name: 'Ortho2009_vue_ensemble_16cm_CC46', |
| 217 | + url: 'https://download.data.grandlyon.com/wms/grandlyon', |
| 218 | + version: '1.3.0', |
| 219 | + crs: 'EPSG:3946', |
| 220 | + format: 'image/jpeg', |
| 221 | + }); |
| 222 | +
|
| 223 | + // Add a WMS imagery layer |
| 224 | + var wmsImageryLayer = new itowns.ColorLayer('wms_imagery', { |
| 225 | + updateStrategy: { |
| 226 | + type: itowns.STRATEGY_DICHOTOMY, |
| 227 | + options: {}, |
| 228 | + }, |
| 229 | + source: wmsImagerySource, |
| 230 | + }); |
| 231 | +
|
| 232 | + view.addLayer(wmsImageryLayer); |
| 233 | +
|
| 234 | + // Add a WMS elevation source |
| 235 | + var wmsElevationSource = new itowns.WMSSource({ |
| 236 | + extent: extent, |
| 237 | + url: 'https://download.data.grandlyon.com/wms/grandlyon', |
| 238 | + name: 'MNT2012_Altitude_10m_CC46', |
| 239 | + crs: 'EPSG:3946', |
| 240 | + width: 256, |
| 241 | + format: 'image/jpeg', |
| 242 | + }); |
| 243 | +
|
| 244 | + // Add a WMS elevation layer |
| 245 | + var wmsElevationLayer = new itowns.ElevationLayer('wms_elevation', { |
| 246 | + useColorTextureElevation: true, |
| 247 | + colorTextureElevationMinZ: 144, |
| 248 | + colorTextureElevationMaxZ: 622, |
| 249 | + source: wmsElevationSource, |
| 250 | + }); |
| 251 | +
|
| 252 | + view.addLayer(wmsElevationLayer); |
| 253 | +
|
| 254 | + // Add 3D Tiles layer |
| 255 | + // This 3D Tiles tileset uses the draco compression that is an |
| 256 | + // extension of gltf. We need to enable it. |
| 257 | + itowns.enableDracoLoader('./libs/draco/'); |
| 258 | +
|
| 259 | + const buildingsSource = new itowns.C3DTilesSource({ |
| 260 | + url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/' + |
| 261 | + 'master/3DTiles/lyon_1_3946_textured_draco/tileset.json', |
| 262 | + }); |
| 263 | +
|
| 264 | + const buildingsLayer = new itowns.C3DTilesLayer('buildings', { |
| 265 | + source: buildingsSource, |
| 266 | + }, view); |
| 267 | + itowns.View.prototype.addLayer.call(view, buildingsLayer); |
| 268 | +
|
| 269 | + const directionalLight = new itowns.THREE.DirectionalLight(0xffffff, 1); |
| 270 | + directionalLight.position.set(-0.9, 0.3, 1); |
| 271 | + directionalLight.updateMatrixWorld(); |
| 272 | + view.scene.add(directionalLight); |
| 273 | +
|
| 274 | + const ambientLight = new itowns.THREE.AmbientLight(0xffffff, 1); |
| 275 | + view.scene.add(ambientLight); |
| 276 | + </script> |
| 277 | + </body> |
| 278 | +</html> |
| 279 | +``` |
0 commit comments