Salome HOME
Access methods for object groups added.
[modules/hydro.git] / src / HYDROData / HYDROData_Bathymetry.cxx
1
2 #include "HYDROData_Bathymetry.h"
3 #include "HYDROData_Document.h"
4 #include "HYDROData_Tool.h"
5 #include "HYDROData_PolylineXY.h"
6
7 #include <boost/math/special_functions/fpclassify.hpp>
8
9 #include <gp_XY.hxx>
10 #include <gp_XYZ.hxx>
11
12 #include <TDataStd_RealArray.hxx>
13 #include <TDataStd_AsciiString.hxx>
14 #include <TDataStd_Integer.hxx>
15
16 #include <QFile>
17 #include <QFileInfo>
18 #include <QPointF>
19 #include <QPolygonF>
20 #include <QStringList>
21
22 #include <math.h>
23
24 // #define _TIMER
25 #ifdef _TIMER
26 #include <OSD_Timer.hxx>
27 #endif
28
29 IMPLEMENT_STANDARD_HANDLE(HYDROData_Bathymetry, HYDROData_IAltitudeObject)
30 IMPLEMENT_STANDARD_RTTIEXT(HYDROData_Bathymetry, HYDROData_IAltitudeObject)
31
32 HYDROData_Bathymetry::HYDROData_Bathymetry()
33 : HYDROData_IAltitudeObject()
34 {
35 }
36
37 HYDROData_Bathymetry::~HYDROData_Bathymetry()
38 {
39 }
40
41 QStringList HYDROData_Bathymetry::DumpToPython( MapOfTreatedObjects& theTreatedObjects ) const
42 {
43   QStringList aResList = dumpObjectCreation( theTreatedObjects );
44   QString aBathymetryName = GetObjPyName();
45
46   aResList << QString( "%1.SetAltitudesInverted( %2 );" )
47               .arg( aBathymetryName ).arg( IsAltitudesInverted() );
48
49   TCollection_AsciiString aFilePath = GetFilePath();
50   aResList << QString( "%1.ImportFromFile( \"%2\" );" )
51               .arg( aBathymetryName ).arg( aFilePath.ToCString() );
52
53   aResList << QString( "" );
54   aResList << QString( "%1.Update();" ).arg( aBathymetryName );
55   aResList << QString( "" );
56
57   return aResList;
58 }
59
60 void HYDROData_Bathymetry::SetAltitudePoints( const AltitudePoints& thePoints )
61 {
62   RemoveAltitudePoints();
63
64   if ( thePoints.IsEmpty() )
65     return;
66
67   // Save coordinates
68   Handle(TDataStd_RealArray) aCoordsArray = 
69     TDataStd_RealArray::Set( myLab.FindChild( DataTag_AltitudePoints ), 0, thePoints.Length() * 3 - 1 );
70
71   AltitudePoints::Iterator anIter( thePoints );
72   for ( int i = 0 ; anIter.More(); ++i, anIter.Next() )
73   {
74     const AltitudePoint& aPoint = anIter.Value();
75
76     aCoordsArray->SetValue( i * 3, aPoint.X() );
77     aCoordsArray->SetValue( i * 3 + 1, aPoint.Y() );
78     aCoordsArray->SetValue( i * 3 + 2, aPoint.Z() );
79   }
80
81   SetToUpdate( true );
82 }
83
84 HYDROData_Bathymetry::AltitudePoints HYDROData_Bathymetry::GetAltitudePoints() const
85 {
86   AltitudePoints aPoints;
87
88   TDF_Label aLabel = myLab.FindChild( DataTag_AltitudePoints, false );
89   if ( aLabel.IsNull() )
90     return aPoints;
91
92   Handle(TDataStd_RealArray) aCoordsArray;
93   if ( !aLabel.FindAttribute( TDataStd_RealArray::GetID(), aCoordsArray ) )
94     return aPoints;
95
96   for ( int i = aCoordsArray->Lower(), n = aCoordsArray->Upper(); i <= n; )
97   {
98     if ( i + 3 > n + 1 )
99       break;
100
101     AltitudePoint aPoint;
102     aPoint.SetX( aCoordsArray->Value( i++ ) );
103     aPoint.SetY( aCoordsArray->Value( i++ ) );
104     aPoint.SetZ( aCoordsArray->Value( i++ ) );
105
106     aPoints.Append( aPoint );
107   }
108
109   return aPoints;
110 }
111
112 void HYDROData_Bathymetry::RemoveAltitudePoints()
113 {
114   TDF_Label aLabel = myLab.FindChild( DataTag_AltitudePoints, false );
115   if ( !aLabel.IsNull() )
116   {
117     aLabel.ForgetAllAttributes();
118     SetToUpdate( true );
119   }
120 }
121
122 void interpolateAltitudeForPoints( const gp_XY&                               thePoint,
123                                    const HYDROData_Bathymetry::AltitudePoint& theFirstPoint,
124                                    const HYDROData_Bathymetry::AltitudePoint& theSecPoint,
125                                    HYDROData_Bathymetry::AltitudePoint&       theResPoint,
126                                    const bool&                                theIsVertical )
127 {
128   double aCoordX = thePoint.X();
129   double aCoordY = thePoint.Y();
130
131   if ( theIsVertical )
132   {
133     aCoordX = theFirstPoint.X();
134
135     if ( !ValuesEquals( theFirstPoint.X(), theSecPoint.X() ) )
136     {
137       // Recalculate X coordinate by equation of line from two points
138       aCoordX = ( ( ( thePoint.Y() - theFirstPoint.Y() ) * ( theSecPoint.X() - theFirstPoint.X() ) ) /
139                   ( theSecPoint.Y() - theFirstPoint.Y() ) ) + theFirstPoint.X();
140     }
141   }
142   else
143   {
144     aCoordY = theFirstPoint.Y();
145
146     if ( !ValuesEquals( theFirstPoint.Y(), theSecPoint.Y() ) )
147     {
148       // Recalculate y by equation of line from two points
149       aCoordY = ( ( ( thePoint.X() - theFirstPoint.X() ) * ( theSecPoint.Y() - theFirstPoint.Y() ) ) /
150                   ( theSecPoint.X() - theFirstPoint.X() ) ) + theFirstPoint.Y();
151     }
152   }
153
154   theResPoint.SetX( aCoordX );
155   theResPoint.SetY( aCoordY );
156
157   // Calculate coefficient for interpolation
158   double aLength = Sqrt( Pow( theSecPoint.Y() - theFirstPoint.Y(), 2 ) +
159                          Pow( theSecPoint.X() - theFirstPoint.X(), 2 ) );
160
161   double aInterCoeff = 0;
162   if ( aLength != 0 )
163    aInterCoeff = ( theSecPoint.Z() - theFirstPoint.Z() ) / aLength;
164
165
166   double aNewLength = Sqrt( Pow( theResPoint.Y() - theFirstPoint.Y(), 2 ) +
167                             Pow( theResPoint.X() - theFirstPoint.X(), 2 ) );
168
169   // Calculate interpolated value
170   double aResVal = theFirstPoint.Z() + aInterCoeff * aNewLength;
171
172   theResPoint.SetZ( aResVal );
173 }
174
175 double HYDROData_Bathymetry::GetAltitudeForPoint( const gp_XY& thePoint ) const
176 {
177   double anInvalidAltitude = GetInvalidAltitude();
178   double aResAltitude = anInvalidAltitude;
179   
180   AltitudePoints anAltitudePoints = GetAltitudePoints();
181   if ( anAltitudePoints.IsEmpty() )
182     return aResAltitude;
183
184   QPolygonF aBoundingRect;
185
186   // Boundary plane
187   // [ 0 (top-left) ]          [ 1 (top-right) ]
188   //                  thePoint
189   // [ 2 (bot-left) ]          [ 3 (bot-right) ] 
190   AltitudePoint aBounds[ 4 ] = { AltitudePoint( -DBL_MAX, -DBL_MAX, anInvalidAltitude ),
191                                  AltitudePoint(  DBL_MAX, -DBL_MAX, anInvalidAltitude ),
192                                  AltitudePoint( -DBL_MAX,  DBL_MAX, anInvalidAltitude ),
193                                  AltitudePoint(  DBL_MAX,  DBL_MAX, anInvalidAltitude ) }; 
194
195   AltitudePoints::Iterator anIter( anAltitudePoints );
196   for ( ; anIter.More(); anIter.Next() )
197   {
198     const AltitudePoint& aPoint = anIter.Value();
199
200     double aDeltaX = Abs( aPoint.X() ) - Abs( thePoint.X() );
201     double aDeltaY = Abs( aPoint.Y() ) - Abs( thePoint.Y() );
202
203     if ( ValuesEquals( aDeltaX, 0.0 ) ) // Both left and right sides
204     {
205       if ( ValuesEquals( aDeltaY, 0.0 ) ) // Both top and bottom sides
206       {
207         aResAltitude = aPoint.Z();
208         return aResAltitude;
209       }
210       else if ( aDeltaY < 0 ) // top side
211       {
212         // top border
213         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 0 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 0 ].Y() ) )
214           aBounds[ 0 ] = aPoint;
215         if ( ValuesLessEquals( aPoint.X(), aBounds[ 1 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 1 ].Y() ) )
216           aBounds[ 1 ] = aPoint;
217       }
218       else
219       {
220         // bottom border
221         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 2 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 2 ].Y() ) )
222           aBounds[ 2 ] = aPoint;
223         if ( ValuesLessEquals( aPoint.X(), aBounds[ 3 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 3 ].Y() ) )
224           aBounds[ 3 ] = aPoint;
225       }
226     }
227     else if ( aDeltaX < 0 ) // left side
228     {
229       if ( ValuesEquals( aDeltaY, 0.0 ) )
230       {
231         // Left border
232         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 0 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 0 ].Y() ) )
233           aBounds[ 0 ] = aPoint;
234         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 2 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 2 ].Y() ) )
235           aBounds[ 2 ] = aPoint;
236       }
237       else if ( aDeltaY < 0 )
238       {
239         // top left corner
240         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 0 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 0 ].Y() ) )
241           aBounds[ 0 ] = aPoint;
242       }
243       else
244       {
245         // bottom left corner
246         if ( ValuesMoreEquals( aPoint.X(), aBounds[ 2 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 2 ].Y() ) )
247           aBounds[ 2 ] = aPoint;
248       }
249     }
250     else // right side
251     {
252       if ( ValuesEquals( aDeltaY, 0.0 ) )
253       {
254         // Right border
255         if ( ValuesLessEquals( aPoint.X(), aBounds[ 1 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 1 ].Y() ) )
256           aBounds[ 1 ] = aPoint;
257         if ( ValuesLessEquals( aPoint.X(), aBounds[ 3 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 3 ].Y() ) )
258           aBounds[ 3 ] = aPoint;
259       }
260       else if ( aDeltaY < 0 )
261       {
262         // top right corner
263         if ( ValuesLessEquals( aPoint.X(), aBounds[ 1 ].X() ) && ValuesMoreEquals( aPoint.Y(), aBounds[ 1 ].Y() ) )
264           aBounds[ 1 ] = aPoint;
265       }
266       else
267       {
268         // bottom right corner
269         if ( ValuesLessEquals( aPoint.X(), aBounds[ 3 ].X() ) && ValuesLessEquals( aPoint.Y(), aBounds[ 3 ].Y() ) )
270           aBounds[ 3 ] = aPoint;
271       }
272     }
273
274     // Update bounding rectangle of our global grid
275     aBoundingRect << QPointF( aPoint.X(), aPoint.Y() );
276   }
277
278   const double LIMIT = 1E300;
279   if( fabs( aBounds[ 0 ].X() ) > LIMIT || fabs( aBounds[ 0 ].Y() ) > LIMIT ||
280       fabs( aBounds[ 1 ].X() ) > LIMIT || fabs( aBounds[ 1 ].Y() ) > LIMIT ||
281       fabs( aBounds[ 2 ].X() ) > LIMIT || fabs( aBounds[ 2 ].Y() ) > LIMIT ||
282       fabs( aBounds[ 3 ].X() ) > LIMIT || fabs( aBounds[ 3 ].Y() ) > LIMIT )
283     return anInvalidAltitude;
284
285
286   // Check if requested point is inside of our bounding rectangle
287   if ( !aBoundingRect.boundingRect().contains( thePoint.X(), thePoint.Y() ) )
288     return aResAltitude;
289
290   // Calculate result altitude for point
291   AltitudePoint aFirstPoint( aBounds[ 0 ] ), aSecPoint( aBounds[ 1 ] );
292
293   // At first we merge top and bottom borders
294   if ( aBounds[ 0 ].Y() != aBounds[ 2 ].Y() || aBounds[ 0 ].X() != aBounds[ 2 ].X() )
295     interpolateAltitudeForPoints( thePoint, aBounds[ 0 ], aBounds[ 2 ], aFirstPoint, true );
296
297   if ( aBounds[ 1 ].Y() != aBounds[ 3 ].Y() || aBounds[ 1 ].X() != aBounds[ 3 ].X() )
298     interpolateAltitudeForPoints( thePoint, aBounds[ 1 ], aBounds[ 3 ], aSecPoint, true );
299
300   AltitudePoint aResPoint( aFirstPoint );
301
302   // At last we merge left and right borders
303   if ( aFirstPoint.Y() != aSecPoint.Y() || aFirstPoint.X() != aSecPoint.X() )
304     interpolateAltitudeForPoints( thePoint, aFirstPoint, aSecPoint, aResPoint, false );
305     
306   aResAltitude = aResPoint.Z();
307
308   return aResAltitude;
309 }
310
311 void HYDROData_Bathymetry::SetFilePath( const TCollection_AsciiString& theFilePath )
312 {
313   TDataStd_AsciiString::Set( myLab.FindChild( DataTag_FilePath ), theFilePath );
314 }
315
316 TCollection_AsciiString HYDROData_Bathymetry::GetFilePath() const
317 {
318   TCollection_AsciiString aRes;
319
320   TDF_Label aLabel = myLab.FindChild( DataTag_FilePath, false );
321   if ( !aLabel.IsNull() )
322   {
323     Handle(TDataStd_AsciiString) anAsciiStr;
324     if ( aLabel.FindAttribute( TDataStd_AsciiString::GetID(), anAsciiStr ) )
325       aRes = anAsciiStr->Get();
326   }
327
328   return aRes;
329 }
330
331 void HYDROData_Bathymetry::SetAltitudesInverted( const bool theIsInverted,
332                                                  const bool theIsUpdate )
333 {
334   bool anIsAltitudesInverted = IsAltitudesInverted();
335   if ( anIsAltitudesInverted == theIsInverted )
336     return;
337
338   TDataStd_Integer::Set( myLab.FindChild( DataTag_AltitudesInverted ), (Standard_Integer)theIsInverted );
339
340   SetToUpdate( true );
341
342   if ( !theIsUpdate )
343     return;
344
345   // Update altitude points
346   AltitudePoints anAltitudePoints = GetAltitudePoints();
347   if ( anAltitudePoints.IsEmpty() )
348     return;
349
350   AltitudePoints::Iterator anIter( anAltitudePoints );
351   for ( ; anIter.More(); anIter.Next() )
352   {
353     AltitudePoint& aPoint = anIter.ChangeValue();
354     aPoint.SetZ( aPoint.Z() * -1 );
355   }
356
357   SetAltitudePoints( anAltitudePoints );
358 }
359
360 bool HYDROData_Bathymetry::IsAltitudesInverted() const
361 {
362   bool aRes = false;
363
364   TDF_Label aLabel = myLab.FindChild( DataTag_AltitudesInverted, false );
365   if ( !aLabel.IsNull() )
366   {
367     Handle(TDataStd_Integer) anIntVal;
368     if ( aLabel.FindAttribute( TDataStd_Integer::GetID(), anIntVal ) )
369       aRes = (bool)anIntVal->Get();
370   }
371
372   return aRes;
373 }
374
375 bool HYDROData_Bathymetry::ImportFromFile( const TCollection_AsciiString& theFileName )
376 {
377   // Try to open the file
378   QFile aFile( theFileName.ToCString() );
379   if ( !aFile.exists() || !aFile.open( QIODevice::ReadOnly ) )
380     return false;
381
382   bool aRes = false;
383
384   QString aFileSuf = QFileInfo( aFile ).suffix().toLower();
385
386   AltitudePoints aPoints;
387
388   // Try to import the file
389   if ( aFileSuf == "xyz" )
390     aRes = importFromXYZFile( aFile, aPoints );
391     
392   // Close the file
393   aFile.close();
394
395   if ( aRes )
396   {
397     // Update file path and altitude points of this Bathymetry
398     SetFilePath( theFileName );
399     SetAltitudePoints( aPoints );
400   }
401
402   return aRes && !aPoints.IsEmpty();
403 }
404
405 bool HYDROData_Bathymetry::importFromXYZFile( QFile&          theFile,
406                                               AltitudePoints& thePoints ) const
407 {
408   if ( !theFile.isOpen() )
409     return false;
410
411   // Strings in file is written as:
412   //  1. X(float) Y(float) Z(float)
413   //  2. X(float) Y(float) Z(float)
414   //  ...
415
416 #ifdef _TIMER
417   OSD_Timer aTimer;
418   aTimer.Start();
419 #endif
420
421   bool anIsAltitudesInverted = IsAltitudesInverted();
422   while ( !theFile.atEnd() )
423   {
424     QString aLine = theFile.readLine().simplified();
425     if ( aLine.isEmpty() )
426       continue;
427
428     QStringList aValues = aLine.split( ' ', QString::SkipEmptyParts );
429     if ( aValues.length() < 3 )
430       return false;
431
432     AltitudePoint aPoint;
433     
434     QString anX = aValues.value( 0 );
435     QString anY = aValues.value( 1 );
436     QString aZ  = aValues.value( 2 );
437
438     bool isXOk = false, isYOk = false, isZOk = false;
439
440     aPoint.SetX( anX.toDouble( &isXOk ) );
441     aPoint.SetY( anY.toDouble( &isYOk ) );
442     aPoint.SetZ( aZ.toDouble( &isZOk ) );
443
444     if ( !isXOk || !isYOk || !isZOk )
445       return false;
446
447     if ( boost::math::isnan( aPoint.X() ) || boost::math::isinf( aPoint.X() ) ||
448          boost::math::isnan( aPoint.Y() ) || boost::math::isinf( aPoint.Y() ) ||
449          boost::math::isnan( aPoint.Z() ) || boost::math::isinf( aPoint.Z() ) )
450       return false;
451
452     // Invert the z value if requested
453     if ( anIsAltitudesInverted )
454       aPoint.SetZ( -aPoint.Z() );
455
456     thePoints.Append( aPoint );
457   }
458
459 #ifdef _TIMER
460   aTimer.Stop();
461   std::ofstream stream( "W:/HYDRO/WORK/log.txt", std::ofstream::out );
462   aTimer.Show( stream );
463 #endif
464
465   return true;
466 }
467
468
469 bool HYDROData_Bathymetry::CreateBoundaryPolyline() const
470 {
471   Handle(HYDROData_Document) aDocument = HYDROData_Document::Document( myLab );
472   Handle_HYDROData_PolylineXY aResult = 
473     Handle_HYDROData_PolylineXY::DownCast( aDocument->CreateObject( KIND_POLYLINEXY ) );
474
475   if( aResult.IsNull() )
476     return false;
477
478   //search free name
479   QString aPolylinePref = GetName() + "_Boundary";
480   QString aPolylineName = HYDROData_Tool::GenerateObjectName( aDocument, aPolylinePref );
481   aResult->SetName( aPolylineName );
482
483   double Xmin = 0.0, Xmax = 0.0, Ymin = 0.0, Ymax = 0.0;
484   bool isFirst = true;
485   AltitudePoints aPoints = GetAltitudePoints();
486
487   AltitudePoints::Iterator anIter( aPoints );
488   for ( ; anIter.More(); anIter.Next() )
489   {
490     const AltitudePoint& aPoint = anIter.Value();
491
492     double x = aPoint.X(), y = aPoint.Y();
493     if( isFirst || x<Xmin )
494       Xmin = x;
495     if( isFirst || x>Xmax )
496       Xmax = x;
497     if( isFirst || y<Ymin )
498       Ymin = y;
499     if( isFirst || y>Ymax )
500       Ymax = y;
501     isFirst = false;
502   }
503
504   aResult->AddSection( "bound", HYDROData_IPolyline::SECTION_POLYLINE, true );
505   aResult->AddPoint( 0, HYDROData_IPolyline::Point( Xmin, Ymin ) );
506   aResult->AddPoint( 0, HYDROData_IPolyline::Point( Xmin, Ymax ) );
507   aResult->AddPoint( 0, HYDROData_IPolyline::Point( Xmax, Ymax ) );
508   aResult->AddPoint( 0, HYDROData_IPolyline::Point( Xmax, Ymin ) );
509   aResult->Update();
510
511   return true;
512 }