source: Swollen/swwreader/swwreader.cpp @ 659

Last change on this file since 659 was 133, checked in by darran, 19 years ago
  • possible fix for Ole's reported -movie <dir> crashes.
  • uninitialized _state.bedslopetexture causing sww.hasBedslopeTexture to inadvertently pass
  • release 20050815 published
File size: 17.9 KB
Line 
1/*
2  SWWReader
3
4  Reader of SWW files from within OpenSceneGraph.
5
6  copyright (C) 2004-2005 Geoscience Australia
7*/
8
9#include <SWWReader.h>
10#include <cstdlib>
11#include <string>
12#include <fstream>
13#include <netcdf.h>
14#include <osg/Notify>
15
16#include <osgDB/Registry>
17#include <osgDB/ReadFile>
18#include <osgDB/FileNameUtils>
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <gdal_priv.h>
23
24
25// compile time defaults
26#define DEFAULT_CULLANGLE 85.0
27#define DEFAULT_ALPHAMIN 0.8
28#define DEFAULT_ALPHAMAX 1.0
29#define DEFAULT_HEIGHTMIN 0.0
30#define DEFAULT_HEIGHTMAX 1.0
31#define DEFAULT_BEDSLOPEOFFSET 0.0
32#define DEFAULT_CULLONSTART false
33
34
35// only constructor, requires netcdf file
36SWWReader::SWWReader(const std::string& filename)
37{
38   // assume failure until end of constructor
39   _valid = false;
40
41   // state initialization
42   _state.bedslopetexturefilename = NULL;
43
44   // netcdf filename
45   _state.swwfilename = new std::string(filename);
46
47   // netcdf open
48   _status.push_back( nc_open(filename.c_str(), NC_NOWRITE, &_ncid) );
49   if (this->_statusHasError()) return;
50
51   // dimension ids
52   _status.push_back( nc_inq_dimid(_ncid, "number_of_volumes", &_nvolumesid) );
53   _status.push_back( nc_inq_dimid(_ncid, "number_of_vertices", &_nverticesid) );
54   _status.push_back( nc_inq_dimid(_ncid, "number_of_points", &_npointsid) );
55   _status.push_back( nc_inq_dimid(_ncid, "number_of_timesteps", &_ntimestepsid) );
56   if (this->_statusHasError()) return;
57
58   // dimension values
59   _status.push_back( nc_inq_dimlen(_ncid, _nvolumesid, &_nvolumes) );
60   _status.push_back( nc_inq_dimlen(_ncid, _nverticesid, &_nvertices) );
61   _status.push_back( nc_inq_dimlen(_ncid, _npointsid, &_npoints) );
62   _status.push_back( nc_inq_dimlen(_ncid, _ntimestepsid, &_ntimesteps) );
63   if (this->_statusHasError()) return;
64
65   // variable ids
66   _status.push_back( nc_inq_varid (_ncid, "x", &_xid) );
67   _status.push_back( nc_inq_varid (_ncid, "y", &_yid) );
68   _status.push_back( nc_inq_varid (_ncid, "z", &_zid) );
69   _status.push_back( nc_inq_varid (_ncid, "volumes", &_volumesid) );
70   _status.push_back( nc_inq_varid (_ncid, "time", &_timeid) );
71   _status.push_back( nc_inq_varid (_ncid, "stage", &_stageid) );
72   if (this->_statusHasError()) return;
73
74   // allocation of variable arrays, destructor responsible for cleanup
75   _px = new float[_npoints];
76   _py = new float[_npoints];
77   _pz = new float[_npoints];
78   _ptime = new float[_ntimesteps];
79   _pvolumes = new unsigned int[_nvertices * _nvolumes];
80   _pstage = new float[_npoints];
81
82   // loading variables from netcdf file
83   _status.push_back( nc_get_var_float (_ncid, _xid, _px) );  // x vertices
84   _status.push_back( nc_get_var_float (_ncid, _yid, _py) );  // y vertices
85   _status.push_back( nc_get_var_float (_ncid, _zid, _pz) );  // bedslope heights
86   _status.push_back( nc_get_var_float (_ncid, _timeid, _ptime) );  // time array
87   _status.push_back( nc_get_var_int (_ncid, _volumesid, (int *) _pvolumes) );  // triangle indices
88   if (this->_statusHasError()) return;
89
90
91   osg::notify(osg::INFO) << "[SWWReader] number of volumes: " << _nvolumes <<  std::endl;
92   osg::notify(osg::INFO) << "[SWWReader] number of vertices: " << _nvertices <<  std::endl;
93   osg::notify(osg::INFO) << "[SWWReader] number of points: " << _npoints <<  std::endl;
94   osg::notify(osg::INFO) << "[SWWReader] number of timesteps: " << _ntimesteps <<  std::endl;
95
96
97   // sww file can optionally contain bedslope texture image filename
98   size_t attlen; // length of text attribute (if it exists)
99   if( nc_inq_attlen(_ncid, NC_GLOBAL, "texture", &attlen) != NC_ENOTATT )
100   {
101      char* texfilename;
102      if( (texfilename = (char*) malloc(attlen+1)) != NULL){
103         int status;
104         status = nc_get_att_text(_ncid, NC_GLOBAL, "texture", texfilename);
105         if( status == NC_NOERR )
106         {
107            texfilename[attlen] = '\0';  // ensure string is terminated, not a requirement for netcdf attributes
108            osg::notify(osg::INFO) << "[SWWReader] embedded image filename: " << texfilename <<  std::endl;
109
110            // if sww isn't in current directory, need to prepend sww path to the bedslope texture
111            if( osgDB::getFilePath(filename) == "" )
112               setBedslopeTexture( std::string(texfilename) );
113            else
114               setBedslopeTexture( osgDB::getFilePath(filename) + std::string("/") + std::string(texfilename) );
115         }
116         free( texfilename );
117      }
118   }
119
120   // sww file can optionally contain georeference offset
121   int status1 = nc_get_att_float(_ncid, NC_GLOBAL, "xllcorner", &_xllcorner);
122   int status2 = nc_get_att_float(_ncid, NC_GLOBAL, "yllcorner", &_yllcorner);
123   if( status1 == NC_NOERR && status2 == NC_NOERR)
124   {
125      osg::notify(osg::INFO) << "[SWWReader] xllcorner: " << _xllcorner <<  std::endl;
126      osg::notify(osg::INFO) << "[SWWReader] yllcorner: " << _yllcorner <<  std::endl;
127   }
128   else
129   {
130      _xllcorner = 0.0;  // default value
131      _yllcorner = 0.0;
132   }
133
134
135   // alpha-scaling defaults, can be overridden after construction by command line parameters
136   _state.alphamin = DEFAULT_ALPHAMIN;
137   _state.alphamax = DEFAULT_ALPHAMAX;
138   _state.heightmin = DEFAULT_HEIGHTMIN;
139   _state.heightmax = DEFAULT_HEIGHTMAX;
140
141   // steepness culling default, can be overridden after construction by command line parameter
142   _state.cullangle = DEFAULT_CULLANGLE;
143   _state.culling = DEFAULT_CULLONSTART;
144
145   // loop index
146   size_t iv;
147
148   // vertex indices
149   unsigned int v1index, v2index, v3index;
150
151   // bedslope range and resultant scale factors (note, these don't take into
152   // account any xllcorner, yllcorner offsets - used during texture coord assignment)
153   float xmin, xmax, xrange;
154   float ymin, ymax, yrange;
155   float zmin, zmax, zrange;
156   float aspect_ratio;
157   xmin = _px[0]; 
158   xmax = _px[0];
159   ymin = _py[0]; 
160   ymax = _py[0];
161   zmin = _pz[0]; 
162   zmax = _pz[0];
163   for( iv=1; iv < _npoints; iv++ )
164   {
165      if( _px[iv] < xmin ) xmin = _px[iv];
166      if( _px[iv] > xmax ) xmax = _px[iv];
167      if( _py[iv] < ymin ) ymin = _py[iv];
168      if( _py[iv] > ymax ) ymax = _py[iv];
169      if( _pz[iv] < zmin ) zmin = _pz[iv];
170      if( _pz[iv] > zmax ) zmax = _pz[iv];
171   }
172
173   xrange = xmax - xmin;
174   yrange = ymax - ymin;
175   zrange = zmax - zmin;
176   aspect_ratio = xrange/yrange;
177   _xscale = 1.0 / xrange;
178   _yscale = 1.0 / yrange;
179   
180   // handle case of a flat bed that doesn't necessarily pass through z=0
181   _zscale = (zrange == 0.0) ? 1.0 : 1.0/zrange;
182
183   _xoffset = xmin;
184   _yoffset = ymin;
185   _zoffset = zmin;
186   _xcenter = 0.5;
187   _ycenter = 0.5;
188   _zcenter = 0.0;
189
190   if( aspect_ratio > 1.0 )
191   {
192      _scale = 1.0/xrange;
193      _ycenter /= aspect_ratio;
194   }
195   else
196   {
197      _scale = 1.0/yrange;
198      _xcenter *= aspect_ratio;
199   }
200
201   osg::notify(osg::INFO) << "[SWWReader] xmin: " << xmin <<  std::endl;
202   osg::notify(osg::INFO) << "[SWWReader] xmax: " << xmax <<  std::endl;
203   osg::notify(osg::INFO) << "[SWWReader] xrange: " << xrange <<  std::endl;
204   osg::notify(osg::INFO) << "[SWWReader] xscale: " << _xscale <<  std::endl;
205   osg::notify(osg::INFO) << "[SWWReader] xcenter: " << _xcenter <<  std::endl;
206   osg::notify(osg::INFO) <<  std::endl;
207
208   osg::notify(osg::INFO) << "[SWWReader] ymin: " << ymin <<  std::endl;
209   osg::notify(osg::INFO) << "[SWWReader] ymax: " << ymax <<  std::endl;
210   osg::notify(osg::INFO) << "[SWWReader] yrange: " << yrange <<  std::endl;
211   osg::notify(osg::INFO) << "[SWWReader] yscale: " << _yscale <<  std::endl;
212   osg::notify(osg::INFO) << "[SWWReader] ycenter: " << _ycenter <<  std::endl;
213   osg::notify(osg::INFO) <<  std::endl;
214
215   osg::notify(osg::INFO) << "[SWWReader] zmin: " << zmin <<  std::endl;
216   osg::notify(osg::INFO) << "[SWWReader] zmax: " << zmax <<  std::endl;
217   osg::notify(osg::INFO) << "[SWWReader] zrange: " << zrange <<  std::endl;
218   osg::notify(osg::INFO) << "[SWWReader] zscale: " << _zscale <<  std::endl;
219   osg::notify(osg::INFO) << "[SWWReader] zcenter: " << _zcenter <<  std::endl;
220   osg::notify(osg::INFO) <<  std::endl;
221
222   // compute triangle connectivity, a list (indexed by vertex number)
223   // of lists (indices of triangles sharing this vertex)
224   _connectivity = std::vector<triangle_list>(_npoints);
225   for (iv=0; iv < _nvolumes; iv++)
226   {
227      v1index = _pvolumes[3*iv+0];
228      v2index = _pvolumes[3*iv+1];
229      v3index = _pvolumes[3*iv+2];
230      _connectivity.at(v1index).push_back(iv);
231      _connectivity.at(v2index).push_back(iv);
232      _connectivity.at(v3index).push_back(iv);
233   }
234
235   // bedslope vertex array, shifting and scaling vertices to unit cube
236   // centred about the origin
237   _bedslopevertices = new osg::Vec3Array;
238   for (iv=0; iv < _npoints; iv++)
239      _bedslopevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, 
240                                               (_py[iv]-_yoffset)*_scale - _ycenter, 
241                                               (_pz[iv]-_zoffset)*_scale - _zcenter - DEFAULT_BEDSLOPEOFFSET) );
242
243   // bedslope index array, pvolumes array indexes into x, y and z
244   _bedslopeindices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, _nvolumes*_nvertices);
245   for (iv=0; iv < _nvolumes*_nvertices; iv++)
246      (*_bedslopeindices)[iv] = _pvolumes[iv];
247
248
249   // calculate bedslope primitive normal and centroid arrays
250   _bedslopenormals = new osg::Vec3Array;
251   _bedslopecentroids = new osg::Vec3Array;
252   osg::Vec3 v1, v2, v3, side1, side2, nrm;
253   for (iv=0; iv < _nvolumes; iv++)
254   {
255      v1index = _pvolumes[3*iv+0];
256      v2index = _pvolumes[3*iv+1];
257      v3index = _pvolumes[3*iv+2];
258
259      v1 = _bedslopevertices->at(v1index);
260      v2 = _bedslopevertices->at(v2index);
261      v3 = _bedslopevertices->at(v3index);
262
263      side1 = v2 - v1;
264      side2 = v3 - v2;
265      nrm = side1^side2;
266      nrm.normalize();
267
268      _bedslopenormals->push_back( nrm );
269      _bedslopecentroids->push_back( (v1+v2+v3)/3.0 );
270   }
271
272   // success!
273   _valid = true;
274
275}
276
277
278
279SWWReader::~SWWReader()
280{
281   _status.push_back( nc_close(_ncid) );
282   delete[]( _px, _py, _pz, _ptime, _pvolumes, _pstage );
283}
284
285
286
287void SWWReader::setBedslopeTexture( std::string filename )
288{
289   osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] filename: " << filename <<  std::endl;
290   _state.bedslopetexturefilename = new std::string(filename);
291   _bedslopegeodata.hasData = false;
292
293   // GDAL Geospatial Image Library
294   GDALDataset *pGDAL;
295   GDALAllRegister();
296   pGDAL = (GDALDataset *) GDALOpen( filename.c_str(), GA_ReadOnly );
297   if( pGDAL == NULL )
298      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL unable to open file: " << filename <<  std::endl;
299   else
300   {
301      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL Driver: "
302                             << pGDAL->GetDriver()->GetDescription() << "/" 
303                             << pGDAL->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME ) 
304                             << std::endl;
305
306      _bedslopegeodata.xresolution = pGDAL->GetRasterXSize();
307      _bedslopegeodata.yresolution = pGDAL->GetRasterYSize();
308      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xresolution: " << _bedslopegeodata.xresolution <<  std::endl;
309      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yresolution: " << _bedslopegeodata.yresolution <<  std::endl;
310
311      // check if image contains geodata
312      double geodata[6];
313      if( pGDAL->GetGeoTransform( geodata ) == CE_None )
314      {
315         _bedslopegeodata.xorigin = geodata[0];
316         _bedslopegeodata.yorigin = geodata[3];
317         _bedslopegeodata.rotation = geodata[2];
318         _bedslopegeodata.xpixel = geodata[1];
319         _bedslopegeodata.ypixel = geodata[5];
320         _bedslopegeodata.hasData = true;
321
322         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xorigin: " << _bedslopegeodata.xorigin <<  std::endl;
323         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yorigin: " << _bedslopegeodata.yorigin <<  std::endl;
324         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xpixel: " << _bedslopegeodata.xpixel <<  std::endl;
325         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] ypixel: " << _bedslopegeodata.ypixel <<  std::endl;
326         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] rotation: " << _bedslopegeodata.rotation <<  std::endl;
327      }
328   }
329}
330
331
332
333osg::Image* SWWReader::getBedslopeTexture()
334{
335   return osgDB::readImageFile( _state.bedslopetexturefilename->c_str() );
336}
337
338
339
340osg::ref_ptr<osg::Vec2Array> SWWReader::getBedslopeTextureCoords()
341{
342   _bedslopetexcoords = new osg::Vec2Array;
343   if( _bedslopegeodata.hasData )  // georeferenced bedslope texture
344   {
345      float xorigin = _bedslopegeodata.xorigin;
346      float yorigin = _bedslopegeodata.yorigin;
347      float xrange = _bedslopegeodata.xpixel * _bedslopegeodata.xresolution;
348      float yrange = _bedslopegeodata.ypixel * _bedslopegeodata.yresolution;
349      for( unsigned int iv=0; iv < _npoints; iv++ )
350         _bedslopetexcoords->push_back( osg::Vec2( (_px[iv] + _xllcorner - xorigin)/xrange, 
351                                                   (_py[iv] + _yllcorner - yorigin)/yrange ) );
352   }
353   else
354   {
355      // decaled using (x,y) locations scaled by extents into range [0,1]
356      for( unsigned int iv=0; iv < _npoints; iv++ )
357         _bedslopetexcoords->push_back( osg::Vec2( (_px[iv]-_xoffset)*_xscale, (_py[iv]-_yoffset)*_yscale ) );
358   }
359
360   return _bedslopetexcoords;
361}
362
363
364
365osg::ref_ptr<osg::Vec3Array> SWWReader::getStageVertexArray(unsigned int index)
366{
367   size_t start[2], count[2], iv;
368   const ptrdiff_t stride[2] = {1,1};
369   start[0] = index;
370   start[1] = 0;
371   count[0] = 1;
372   count[1] = _npoints;
373
374   // empty array for storing list of steep triangles
375   osg::ref_ptr<osg::IntArray> steeptri = new osg::IntArray;
376
377   // stage heights from netcdf file (x and y are same as bedslope)
378   int status = nc_get_vars_float (_ncid, _stageid, start, count, stride, _pstage);
379
380   // load stage vertex array, scaling and shifting vertices to lie in the unit cube
381   _stagevertices = new osg::Vec3Array;
382   for (iv=0; iv < _npoints; iv++)
383      _stagevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, 
384                                            (_py[iv]-_yoffset)*_scale - _ycenter, 
385                                            (_pstage[iv]-_zoffset)*_scale - _zcenter) );
386
387   // stage index, per primitive normal and centroid arrays
388   _stageprimitivenormals = new osg::Vec3Array;
389   _stagecentroids = new osg::Vec3Array;
390   osg::Vec3 v1b, v2b, v3b;
391   osg::Vec3 v1s, v2s, v3s;
392   osg::Vec3 side1, side2, nrm;
393   unsigned int v1index, v2index, v3index;
394
395   // cullangle given in degrees, test is against dot product
396   float cullthreshold = cos(osg::DegreesToRadians(_state.cullangle));
397
398   // over all stage triangles
399   for (iv=0; iv < _nvolumes; iv++)
400   {
401      v1index = _pvolumes[3*iv+0];
402      v2index = _pvolumes[3*iv+1];
403      v3index = _pvolumes[3*iv+2];
404
405      v1s = _stagevertices->at(v1index);
406      v2s = _stagevertices->at(v2index);
407      v3s = _stagevertices->at(v3index);
408      v1b = _bedslopevertices->at(v1index);
409      v2b = _bedslopevertices->at(v2index);
410      v3b = _bedslopevertices->at(v3index);
411
412      // current triangle primitive normal
413      side1 = v2s - v1s;
414      side2 = v3s - v2s;
415      nrm = side1^side2;
416      nrm.normalize();
417
418      // store centroid and primitive normal
419      _stagecentroids->push_back( (v1s+v2s+v3s)/3.0 );
420      _stageprimitivenormals->push_back( nrm );
421
422      // identify steep triangles, store index
423      if( fabs(nrm * osg::Vec3f(0,0,1)) < cullthreshold )
424         steeptri->push_back( iv );
425   }
426
427   // stage height above bedslope mapped as alpha value
428   //      alpha = min( a(h-hmin) + alphamin, alphamax),  h >= hmin
429   //      alpha = 0,                                     h < hmin
430   // where a = (alphamax-alphamin)/(hmax-hmin)
431   float alpha, height, alphascale;
432   alphascale = (_state.alphamax - _state.alphamin) / (_state.heightmax - _state.heightmin);
433   _stagecolors = new osg::Vec4Array;
434   for (iv=0; iv < _npoints; iv++)
435   {
436      // water height above corresponding bedslope
437      height = _stagevertices->at(iv).z() - _bedslopevertices->at(iv).z();
438       
439      if (height < _state.heightmin)
440         alpha = 0.0;
441      else 
442      {
443         alpha = alphascale * (height - _state.heightmin) + _state.alphamin;
444         if( alpha > _state.alphamax ) 
445            alpha = _state.alphamax;
446      }
447
448      _stagecolors->push_back( osg::Vec4( 1.0, 1.0, 1.0, alpha ) );
449   }
450
451   // steep triangle vertices should have alpha=0, overwrite such vertex colours
452   if( _state.culling )
453   {
454      //std::cout << "culling on step: " << index << "  steeptris: " << steeptri->size() << std::endl;
455      for (iv=0; iv < steeptri->size() ; iv++)
456      {
457         int tri = steeptri->at(iv);
458         v1index = _pvolumes[3*tri+0];
459         v2index = _pvolumes[3*tri+1];
460         v3index = _pvolumes[3*tri+2];
461
462         _stagecolors->at(v1index) = osg::Vec4( 1, 1, 1, 0 );
463         _stagecolors->at(v2index) = osg::Vec4( 1, 1, 1, 0 );
464         _stagecolors->at(v3index) = osg::Vec4( 1, 1, 1, 0 );
465      }
466   }
467
468   // per-vertex normals calculated as average of primitive normals
469   // from contributing triangles
470   _stagevertexnormals = new osg::Vec3Array;
471   int num_shared_triangles, triangle_index;
472   for (iv=0; iv < _npoints; iv++)
473   {
474      nrm.set(0,0,0);
475      num_shared_triangles = _connectivity.at(iv).size();
476      for (int i=0; i<num_shared_triangles; i++ )
477      {
478         triangle_index = _connectivity.at(iv).at(i);
479         nrm += _stageprimitivenormals->at(triangle_index);
480      }
481
482      nrm = nrm / num_shared_triangles;  // average
483      nrm.normalize();
484      _stagevertexnormals->push_back(nrm);
485   }
486
487   return _stagevertices;
488}
489
490
491
492
493bool SWWReader::_statusHasError()
494{
495   bool haserror = false;  // assume success, trap failure
496
497   std::vector<int>::iterator iter;
498   for (iter=_status.begin(); iter != _status.end(); iter++)
499   {
500      if (*iter != NC_NOERR)
501      {
502         osg::notify(osg::WARN) << nc_strerror(*iter) <<  std::endl;
503         haserror = true;
504         nc_close(_ncid);
505         break;
506      }
507   }
508
509   // on return start gathering result values afresh
510   _status.clear();
511
512   return haserror;
513}
Note: See TracBrowser for help on using the repository browser.