source: Swollen/swwreader/swwreader.cpp @ 116

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