Salome HOME
Allocation of vtkEDFCutter in a separate vtkTools library
[modules/visu.git] / src / CONVERTOR / VISU_TableReader.cxx
1 // Copyright (C) 2007-2011  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 //  VISU OBJECT : interactive object for VISU entities implementation
21 //  File:
22 //  Author:
23 //  Module : VISU
24 //
25 #include "VISU_TableReader.hxx"
26
27 #include <QFileInfo>
28 #include <QString>
29 #include <QRegExp>
30 #include <QFile>
31 #include <QStringList>
32
33 #include <fstream>
34 #include <iostream>
35 #include <sstream>
36
37 #include <vtkPoints.h>
38 #include <vtkDoubleArray.h>
39 #include <vtkPointData.h>
40 #include <vtkCellData.h>
41 #include <vtkPolyData.h>
42
43 #include <vtkStructuredGrid.h>
44 #include <vtkStructuredGridGeometryFilter.h>
45
46 #include <Basics_Utils.hxx>
47
48 #ifdef _DEBUG_
49 static int MYDEBUG = 0;
50 #else
51 static int MYDEBUG = 0;
52 #endif
53
54
55 //---------------------------------------------------------------
56 int
57 VISU::TTable2D
58 ::Check()
59 {
60   if ( myRows.empty() ) 
61     return 0;
62
63   int iEnd = myRows[0].myValues.size();
64   if ( iEnd == 0 )
65     return 0;
66
67   if ( myColumnTitles.size() != iEnd ) 
68     myColumnTitles.resize( iEnd );
69
70   if ( myColumnUnits.size() != iEnd )
71     myColumnUnits.resize( iEnd );
72
73   int jEnd = myRows.size();
74   for ( int j = 0; j < jEnd; j++ )
75     if ( myRows[j].myValues.size() != iEnd )
76       return 0;
77
78   return 1;
79 }
80
81
82 //---------------------------------------------------------------
83 void
84 VISU::TTable2D
85 ::getColumns(VISU::TTable2D& theTable2D) const
86 {
87   TRows& aRows = theTable2D.myRows;
88   aRows.clear();
89   if ( myRows.empty() )
90     return;
91
92   int jEnd = myRows.size();
93
94   //Define Titles & Units
95   theTable2D.myColumnTitles.resize(jEnd);
96   theTable2D.myColumnUnits.resize(jEnd);
97   for ( int j = 0; j < jEnd; j++ ) {
98     theTable2D.myColumnTitles[j] = myRows[j].myTitle;
99     theTable2D.myColumnUnits[j] = myRows[j].myUnit;
100   }
101
102   //Define Rows
103   int iEnd = myRows[0].myValues.size();
104   for ( int i = 0; i < iEnd; i++ ) {
105     TRow aNewRow;
106     aNewRow.myTitle = myColumnTitles[i];
107     aNewRow.myUnit = myColumnUnits[i];
108     aNewRow.myValues.resize(jEnd);
109     for ( int j = 0; j < jEnd; j++ ) {
110       aNewRow.myValues[j] = myRows[j].myValues[i];
111     }
112     aRows.push_back(aNewRow);
113   }
114 }
115
116
117 //---------------------------------------------------------------
118 namespace
119 {
120   int getLine( std::ifstream& theStmIn, QString& theString )
121   {
122     char tmp;
123     std::ostringstream aStrOut;
124
125     while ( theStmIn.get( tmp ) ) {
126       aStrOut<<tmp;
127       if ( tmp == '\n' ) 
128         break;
129     }
130
131     aStrOut<<std::ends;
132     theString = aStrOut.str().c_str();
133
134     return !theStmIn.eof();
135   }
136
137   //=======================================================================
138   //function : findNextCell
139   //purpose  : auxilary for ImportCSVTable
140   //=======================================================================
141   bool findNextCell(std::ifstream& aStmIn, QString& aStr,
142                    QString& aCell, const char theSeparator)
143   {
144     aCell = "";
145     int index, tmpind = 0;
146     if( aStr.at(0) == theSeparator ) {
147       aStr.remove(0,1);
148       aStr = aStr.trimmed();
149       if(aStr.size()==0) return true;
150     }
151     QString aTmp = aStr;
152     if( aTmp.at(0) == '"' ) {
153       // find closed "
154       while( !aStmIn.eof() ) {
155         tmpind = aTmp.indexOf('"',1);
156         if( tmpind < 0 ) {
157           while( !aStmIn.eof() ) {
158             aCell.push_back(aTmp);
159             aCell.push_back('\n');
160             getLine( aStmIn, aTmp );
161             tmpind = aTmp.indexOf('"',1);
162             if( tmpind >= 0 ) {
163               break;
164             }
165           }
166         }
167         if(tmpind < 0)
168           return false;
169         // read next symbol
170         if( aTmp.at(tmpind+1) == '"' ) {
171           // "" is found => need to continue
172           aCell.push_back(aTmp.left(tmpind+1));
173           aTmp = aTmp.mid(tmpind+2);
174         }
175         else if( aTmp.at(tmpind+1) == theSeparator ) {
176           index = tmpind+1;
177           break;
178         }
179         else {
180           return false;
181         }
182       }
183     }
184     else {
185       // find index for separator
186       index = aTmp.indexOf( theSeparator );
187     }
188     if( index < 0 ) {
189       aCell += aTmp;
190       aStr = "";
191     }
192     else {
193       if(index>0) aCell += aTmp.left(index);
194       aStr = aTmp.mid(index).trimmed();
195     }
196     if( aCell.size()>0 && aCell.at(0) == '"' ) {
197       // remove first and last symbols
198       int last = aCell.size()-1;
199       aCell.remove(last,1);
200       aCell.remove(0,1);
201     }
202     // replace "" to " in aCell
203     QChar ctmp = '"';
204     QString tmp(ctmp);
205     tmp += ctmp;
206     index = aCell.indexOf(tmp);
207     while(index>=0) {
208       aCell.remove(index,1);
209       index = aCell.indexOf(tmp);
210     }
211     return true;
212   }
213
214 }
215
216
217 //---------------------------------------------------------------
218 void 
219 VISU::ImportTables( const char* theFileName, TTableContainer& theContainer,
220                     bool theFirstStrAsTitle)
221 {
222   std::ifstream aStmIn;
223   QFileInfo aFileInfo( theFileName );
224   if( !aFileInfo.isFile() || !aFileInfo.isReadable() || !aFileInfo.size() )
225     return;
226
227   QString tmp(theFileName);
228   tmp = tmp.trimmed();
229   tmp = tmp.right(3).trimmed();
230
231   if( tmp == QString("csv") ) {
232     const char separator = ',';
233     ImportCSVTable(theFileName, theContainer, theFirstStrAsTitle, separator);
234     return;
235   }
236
237   aStmIn.open( theFileName );
238   QString aTmp;
239   do {
240     // find beginning of table (tables are separated by empty lines)
241     while( getLine( aStmIn, aTmp ) && aTmp.trimmed() == "" );
242
243     PTableIDMapper aTableIDMapper( new TTableIDMapper() );
244     TTable2D& aTable2D = *aTableIDMapper;
245     if(MYDEBUG) std::cout << "New table is found" << std::endl;
246
247     bool IsFirst = true;
248     while( !aStmIn.eof() && aTmp.trimmed() != "" ){
249       QString data = aTmp.trimmed();
250       QString cmt = "";
251       QString keyword = "";
252       // split string to data and comment (comment starts from '#' symbol)
253       int index = aTmp.indexOf( "#" );
254       if ( index >= 0 ) {
255         data = aTmp.left( index ).trimmed();
256         cmt = aTmp.mid( index+1 ).trimmed();
257       }
258       // if comment is not empty, try to get keyword from it (separated by ':' symbol)
259       if ( !cmt.isEmpty() ) {
260         int index1 = cmt.indexOf( ":" );
261         if ( index1 >= 0 ) {
262           QString tmpstr = cmt.left( index1 ).trimmed();
263           if ( tmpstr == QString( "TITLE" ) ||
264                tmpstr == QString( "COLUMN_TITLES" ) ||
265                tmpstr == QString( "COLUMN_UNITS" ) ||
266                tmpstr == QString( "COMMENT" ) ) {
267             keyword = tmpstr;
268             cmt = cmt.mid( index1+1 ).trimmed();
269           }
270         }
271       }
272       // if data is empty, process only comment
273       if ( data.isEmpty() ) {
274         // if keyword is found, try to process it
275         // elsewise it is a simple comment, just ignore it
276         if ( !keyword.isEmpty() ) {
277           if ( keyword == QString( "TITLE" ) ) {
278             QString title = cmt;
279             if ( aTable2D.myTitle != "" )
280               title = QString( aTable2D.myTitle.c_str() ) + QString( " " ) + title;
281             if(MYDEBUG) std::cout << "...Table TITLE is: " << title.toLatin1().constData() << std::endl;
282             aTable2D.myTitle = title.toLatin1().constData();
283           }
284           else if ( keyword == QString( "COLUMN_TITLES" ) ) {
285             // comment may contain column headers
286             QStringList aStrList = cmt.split( "|", QString::SkipEmptyParts );
287             if(MYDEBUG) std::cout << "...Column TITLES are: ";
288             for ( int i = 0; i < aStrList.count(); i++ ) {
289               QString tmpstr = aStrList[ i ].trimmed();
290               if(MYDEBUG) std::cout << tmpstr.toLatin1().constData() << " ";
291               aTable2D.myColumnTitles.push_back( tmpstr.toLatin1().constData() );
292             }
293             if(MYDEBUG) std::cout << std::endl;
294           }
295           else if ( keyword == QString( "COLUMN_UNITS" ) ) {
296             // comment may contain column units
297             QStringList aStrList = cmt.split( " ", QString::SkipEmptyParts );
298             if(MYDEBUG) std::cout << "...Column UNITS are: ";
299             for ( int i = 0; i < aStrList.count(); i++ ) {
300               QString tmpstr = aStrList[ i ].trimmed();
301               if(MYDEBUG) std::cout << tmpstr.toLatin1().constData() << " ";
302               aTable2D.myColumnUnits.push_back( tmpstr.toLatin1().constData() );
303             }
304             if(MYDEBUG) std::cout << std::endl;
305           }
306           else if ( keyword == QString( "COMMENT" ) ) {
307             // keyword 'COMMENT' processing can be here
308             // currently it is ignored
309             if(MYDEBUG) std::cout << "...COMMENT: " << cmt.toLatin1().constData() << std::endl;
310           }
311         }
312         else {
313           if(MYDEBUG) std::cout << "...comment: " << cmt.toLatin1().constData() << std::endl;
314           // simple comment processing can be here
315           // currently it is ignored
316         }
317       }
318       // if data is not empty, try to process it
319       else {
320         TTable2D::TRow aRow;
321         if(MYDEBUG) std::cout << "...New row is found: " << std::endl;
322         QString datar1 = data.replace(QRegExp("\t"), " ");
323         QStringList aValList = datar1.split( " ", QString::SkipEmptyParts );
324         if( aTable2D.myColumnTitles.size()==0 && IsFirst && theFirstStrAsTitle ) {
325           for ( int i = 0; i < aValList.count(); i++ ) {
326             QString tmpstr = aValList[ i ].trimmed();
327             aTable2D.myColumnTitles.push_back( tmpstr.toLatin1().constData() );
328           }
329         }
330         else {
331           if ( !cmt.isEmpty() ) {
332             aRow.myTitle = cmt.toLatin1().constData();
333             if(MYDEBUG) std::cout << "......ROW TITLE is: " << cmt.toLatin1().constData() << std::endl;
334           }
335           //QString datar1 = data.replace(QRegExp("\t"), " ");
336           //QStringList aValList = datar1.split( " ", QString::SkipEmptyParts );
337           for ( int i = 0; i < aValList.count(); i++ ) {
338             if ( aValList[i].trimmed() != "" ) {
339               TTable2D::TValue aVal = aValList[i].trimmed().toLatin1().constData();
340               aRow.myValues.push_back( aVal );
341             }
342           }
343           if( aRow.myValues.size() > 0 )
344             aTable2D.myRows.push_back( aRow );
345         }
346         IsFirst = false;
347         // ************** OLD CODE ******************
348         /*
349         TValue aVal;
350         istringstream aStream( data );
351         aStream.precision( STRPRECISION );
352         while( aStream >> aVal ) {
353           aRow.myValues.push_back( aVal );
354         }
355         if( aRow.myValues.size() > 0 )
356           aTable2D.myRows.push_back( aRow );
357         */
358         // ************** OLD CODE ******************
359       }
360       getLine( aStmIn, aTmp );
361     }
362     if( aTable2D.Check() ) {
363       if(MYDEBUG) std::cout << "aTable2D is checked OK " << aTable2D.myTitle << std::endl;
364       theContainer.push_back( aTableIDMapper );
365     }
366   } while ( !aStmIn.eof() );
367   aStmIn.close();
368
369   if(MYDEBUG) std::cout << "After close" << std::endl;
370 }
371
372
373 //=======================================================================
374 //function : ImportCSVTable
375 //purpose  : 
376 //=======================================================================
377 void VISU::ImportCSVTable(const char* theFileName, TTableContainer& theContainer,
378                           bool theFirstStrAsTitle, const char theSeparator)
379 {
380   std::ifstream aStmIn;
381   QFileInfo aFileInfo( theFileName );
382   if( !aFileInfo.isFile() || !aFileInfo.isReadable() || !aFileInfo.size() )
383     return;
384   aStmIn.open( theFileName );
385   QString aTmp;
386   do {
387     // find beginning of table (tables are separated by empty lines)
388     while( getLine( aStmIn, aTmp ) && aTmp.trimmed() == "" );
389
390     PTableIDMapper aTableIDMapper( new TTableIDMapper() );
391     TTable2D& aTable2D = *aTableIDMapper;
392     if(MYDEBUG) std::cout << "New table is found" << std::endl;
393
394     bool IsFirst = true;
395     QStringList aValList;
396
397     while( !aStmIn.eof() ) {
398       QString aCell = "";
399       if( !( findNextCell(aStmIn, aTmp, aCell, theSeparator)) ) {
400         return;
401       }
402       if( aTmp.size()==0 ) {
403         // make table row
404         aValList.push_back(aCell);
405         if( IsFirst && theFirstStrAsTitle ) {
406           for ( int i = 0; i < aValList.count(); i++ ) {
407             aTable2D.myColumnTitles.push_back( aValList[i].trimmed().toLatin1().constData() );
408           }
409         }
410         else {
411           TTable2D::TRow aRow;
412           for ( int i = 0; i < aValList.count(); i++ ) {
413             if ( aValList[i].trimmed() != "" ) {
414               TTable2D::TValue aVal = aValList[i].trimmed().toLatin1().constData();
415               aRow.myValues.push_back( aVal );
416             }
417             else {
418               aRow.myValues.push_back( "Empty" );
419             }
420           }
421           if( aRow.myValues.size() > 0 ) {
422             aTable2D.myRows.push_back( aRow );
423           }
424         }
425         // clear list of values and read next string
426         aValList.clear();
427         getLine( aStmIn, aTmp );
428         IsFirst = false;
429       }
430       else {
431         // put value to table cell
432         aValList.push_back(aCell);
433       }
434     }
435
436     if( aTable2D.Check() ) {
437       if(MYDEBUG) std::cout << "aTable2D is checked OK " << aTable2D.myTitle << std::endl;
438       theContainer.push_back( aTableIDMapper );
439     }
440
441   } while ( !aStmIn.eof() );
442   aStmIn.close();
443
444   if(MYDEBUG) std::cout << "After close" << std::endl;
445 }
446
447
448 //---------------------------------------------------------------
449 VISU::TTableIDMapper
450 ::TTableIDMapper():
451   myOutput( vtkPolyData::New() ),
452   myXAxisPosition( -1 )
453 {}
454
455 VISU::TTableIDMapper
456 ::~TTableIDMapper()
457 {
458   myOutput->Delete();
459 }
460
461 vtkPolyData*
462 VISU::TTableIDMapper
463 ::GetPolyDataOutput()
464 {
465   if ( myXAxisPosition == -1 )
466     SetXAxisPosition( 0 );
467
468   return myOutput;
469 }
470
471 long unsigned int
472 VISU::TTableIDMapper
473 ::GetMemorySize()
474 {
475   return myOutput->GetActualMemorySize() * 1024;
476 }
477
478 void
479 VISU::TTableIDMapper
480 ::SetXAxisPosition( vtkIdType theAxisPosition )
481 {
482   // Set "C" numeric locale to import numbers correctly
483   Kernel_Utils::Localizer loc;
484
485   if ( myXAxisPosition == theAxisPosition || !Check() )
486     return;
487
488   myOutput->Initialize();
489
490   if ( !Check() )
491     return;
492
493   TTable2D aTable2D;
494   getColumns( aTable2D );
495   
496   vtkIdType aXSize = aTable2D.myRows[0].myValues.size();
497
498   // It is necessary to decrease the size at 1 take intoa account X axis
499   vtkIdType anYSize = aTable2D.myRows.size() - 1; 
500
501   vtkIdType aNbPoints = aXSize * anYSize;
502
503   std::vector<double> anXAxis(aXSize);
504   const TTable2D::TValues& aValues = aTable2D.myRows[theAxisPosition].myValues;
505   for ( vtkIdType aX = 0; aX < aXSize; aX++ )
506     anXAxis[aX] = atof( aValues[aX].c_str() );
507
508   double aXRange = anXAxis[aXSize - 1] - anXAxis[0];
509   double anYDelta = aXRange / anYSize;
510   std::vector<double> anYAxis(anYSize);
511   for ( vtkIdType anY = 0; anY < anYSize; anY++ )
512     anYAxis[anY] = anY * anYDelta;
513
514   vtkPoints* aPoints = vtkPoints::New();
515   aPoints->SetNumberOfPoints( aNbPoints );
516
517   vtkIntArray *aPointsIDMapper = vtkIntArray::New();
518   aPointsIDMapper->SetName("VISU_POINTS_MAPPER");
519   aPointsIDMapper->SetNumberOfComponents(2);
520   aPointsIDMapper->SetNumberOfTuples(aNbPoints);
521   int *aPointsIDMapperPtr = aPointsIDMapper->GetPointer(0);
522
523   //vtkIntArray *aCellIDMapper = vtkIntArray::New();
524   //aCellIDMapper->SetName("VISU_POINTS_MAPPER");
525   //aCellIDMapper->SetNumberOfComponents(2);
526   //aCellIDMapper->SetNumberOfTuples(aNbPoints);
527   //int *aCellIDMapperPtr = aCellIDMapper->GetPointer(0);
528
529   for ( vtkIdType aY = 0, aPntId = 0; aY < anYSize; aY++ ) {
530     for ( vtkIdType aX = 0; aX < aXSize; aX++, aPntId++ ) {
531       aPoints->SetPoint( aPntId, anXAxis[aX], anYAxis[aY], 0.0 );
532
533       *aPointsIDMapperPtr++ = aPntId;
534       *aPointsIDMapperPtr++ = 0;
535
536       //*aCellIDMapperPtr++ = aPntId;
537       //*aCellIDMapperPtr++ = 0;
538     }
539   }
540
541   std::vector<TValues> anYData;
542   for ( vtkIdType anY = 0; anY < anYSize + 1; anY++ ) {
543     if ( anY == theAxisPosition )
544       continue;
545     anYData.push_back( aTable2D.myRows[anY].myValues );
546   }
547
548   vtkDoubleArray* aScalars = vtkDoubleArray::New();
549   aScalars->SetNumberOfComponents( 1 );
550   aScalars->SetNumberOfTuples( aNbPoints );
551   double *aScalarsPtr = aScalars->GetPointer(0);
552   for ( vtkIdType anY = 0; anY < anYSize; anY++ ) {
553     const TTable2D::TValues& aValues = anYData[anY];
554     for ( vtkIdType aX = 0; aX < aXSize; aX++ ) {
555       double aValue = atof( aValues[aX].c_str() );
556       *aScalarsPtr++ = aValue;
557     }
558   }
559
560   vtkStructuredGrid* aStructuredGrid = vtkStructuredGrid::New();
561   aStructuredGrid->SetPoints( aPoints );
562   aPoints->Delete();
563
564   aStructuredGrid->SetDimensions( aXSize, anYSize, 1 );
565
566   aStructuredGrid->GetPointData()->AddArray( aPointsIDMapper );
567   aPointsIDMapper->Delete();
568
569   //aStructuredGrid->GetCellData()->AddArray( aCellIDMapper );
570   //aCellIDMapper->Delete();
571
572   aStructuredGrid->GetPointData()->SetScalars( aScalars );
573   aScalars->Delete();
574
575   vtkStructuredGridGeometryFilter* aFilter = vtkStructuredGridGeometryFilter::New();
576   aFilter->SetInput( aStructuredGrid );
577   aFilter->Update();
578   myOutput->ShallowCopy( aFilter->GetOutput() );
579   aFilter->Delete();
580 }
581