1
2 ///////////////////////////////////////////////////////////
3 // //
4 // SAGA //
5 // //
6 // System for Automated Geoscientific Analyses //
7 // //
8 // Tool Library //
9 // Grid_Calculus //
10 // //
11 //-------------------------------------------------------//
12 // //
13 // Grid_Calculator.cpp //
14 // //
15 // Copyright (C) 2003 by //
16 // Andre Ringeler //
17 // //
18 //-------------------------------------------------------//
19 // //
20 // This file is part of 'SAGA - System for Automated //
21 // Geoscientific Analyses'. SAGA is free software; you //
22 // can redistribute it and/or modify it under the terms //
23 // of the GNU General Public License as published by the //
24 // Free Software Foundation, either version 2 of the //
25 // License, or (at your option) any later version. //
26 // //
27 // SAGA is distributed in the hope that it will be //
28 // useful, but WITHOUT ANY WARRANTY; without even the //
29 // implied warranty of MERCHANTABILITY or FITNESS FOR A //
30 // PARTICULAR PURPOSE. See the GNU General Public //
31 // License for more details. //
32 // //
33 // You should have received a copy of the GNU General //
34 // Public License along with this program; if not, see //
35 // <http://www.gnu.org/licenses/>. //
36 // //
37 //-------------------------------------------------------//
38 // //
39 // e-mail: aringel@gwdg.de //
40 // //
41 // contact: Andre Ringeler //
42 // Institute of Geography //
43 // University of Goettingen //
44 // Goldschmidtstr. 5 //
45 // 37077 Goettingen //
46 // Germany //
47 // //
48 ///////////////////////////////////////////////////////////
49
50 //---------------------------------------------------------
51 #include "Grid_Calculator.h"
52
53
54 ///////////////////////////////////////////////////////////
55 // //
56 // //
57 // //
58 ///////////////////////////////////////////////////////////
59
60 //---------------------------------------------------------
61 #ifdef _SAGA_LINUX
_finite(double val)62 bool _finite(double val)
63 {
64 return( true );
65 }
66 #endif
67
68
69 ///////////////////////////////////////////////////////////
70 // //
71 // //
72 // //
73 ///////////////////////////////////////////////////////////
74
75 //---------------------------------------------------------
76 double CGrid_Calculator_Base::m_NoData_Value = -99999;
77
78 //---------------------------------------------------------
CGrid_Calculator_Base(void)79 CGrid_Calculator_Base::CGrid_Calculator_Base(void)
80 {
81 CSG_String s(_TW(
82 "The Grid Calculator calculates a new grid based on existing grids and a mathematical formula. "
83 "The grid variables in the formula begin with the letter 'g' followed by a position index, "
84 "which corresponds to the order of the grids in the input grid list "
85 "(i.e.: g1, g2, g3, ... correspond to the first, second, third, ... grid in list). "
86 "Grids from other systems than the default one can be addressed likewise using the letter 'h' "
87 "(h1, h2, h3, ...), which correspond to the \'Grids from different Systems\' list.\n"
88 "\n"
89 "Example:\t sin(g1) * g2 + 2 * h1\n"
90 "\n"
91 "The following operators are available for the formula definition:\n"
92 ));
93
94 const CSG_String Operators[5][2] =
95 {
96 { "xpos(), ypos()" , _TL("Get the x/y coordinates for the current cell" ) },
97 { "col(), row()" , _TL("Get the current cell's column/row index (zero based)" ) },
98 { "ncols(), nrows()", _TL("Get the number of columns/rows" ) },
99 { "nodata()" , _TL("Returns resulting grid's no-data value" ) },
100 { "", "" }
101 };
102
103 s += CSG_Formula::Get_Help_Operators(true, Operators);
104
105 Set_Description(s);
106
107 //-----------------------------------------------------
108 Parameters.Add_Choice("",
109 "RESAMPLING", _TL("Resampling"),
110 _TL(""),
111 CSG_String::Format("%s|%s|%s|%s",
112 _TL("Nearest Neighbour"),
113 _TL("Bilinear Interpolation"),
114 _TL("Bicubic Spline Interpolation"),
115 _TL("B-Spline Interpolation")
116 ), 3
117 );
118
119 Parameters.Add_String("",
120 "FORMULA" , _TL("Formula"),
121 _TL(""),
122 "(g1 - g2) / (g1 + g2)"
123 );
124
125 Parameters.Add_String("",
126 "NAME" , _TL("Name"),
127 _TL(""),
128 _TL("Calculation")
129 );
130
131 Parameters.Add_Bool("NAME",
132 "FNAME" , _TL("Take Formula"),
133 _TL(""),
134 false
135 );
136
137 Parameters.Add_Bool("",
138 "USE_NODATA", _TL("Use No-Data"),
139 _TL("Check this in order to include no-data cells in the calculation."),
140 false
141 );
142
143 Parameters.Add_Choice("",
144 "TYPE" , _TL("Data Type"),
145 _TL(""),
146 CSG_String::Format("%s|%s|%s|%s|%s|%s|%s|%s|%s",
147 SG_Data_Type_Get_Name(SG_DATATYPE_Bit ).c_str(),
148 SG_Data_Type_Get_Name(SG_DATATYPE_Byte ).c_str(),
149 SG_Data_Type_Get_Name(SG_DATATYPE_Char ).c_str(),
150 SG_Data_Type_Get_Name(SG_DATATYPE_Word ).c_str(),
151 SG_Data_Type_Get_Name(SG_DATATYPE_Short ).c_str(),
152 SG_Data_Type_Get_Name(SG_DATATYPE_DWord ).c_str(),
153 SG_Data_Type_Get_Name(SG_DATATYPE_Int ).c_str(),
154 SG_Data_Type_Get_Name(SG_DATATYPE_Float ).c_str(),
155 SG_Data_Type_Get_Name(SG_DATATYPE_Double).c_str()
156 ), 7
157 );
158
159 //-----------------------------------------------------
160 m_Formula.Add_Function("nodata", (TSG_Formula_Function_1)CGrid_Calculator_Base::Get_NoData_Value, 0, false);
161 }
162
163
164 ///////////////////////////////////////////////////////////
165 // //
166 ///////////////////////////////////////////////////////////
167
168 //---------------------------------------------------------
On_Parameter_Changed(CSG_Parameters * pParameters,CSG_Parameter * pParameter)169 int CGrid_Calculator_Base::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
170 {
171 if( pParameter->Cmp_Identifier("FORMULA")
172 || pParameter->Cmp_Identifier("FNAME" ) )
173 {
174 if( (*pParameters)("FNAME")->asBool() )
175 {
176 pParameters->Set_Parameter("NAME", CSG_String::Format("%s [%s]", _TL("Calculation"), (*pParameters)("FORMULA")->asString()));
177 }
178 }
179
180 return( CSG_Tool_Grid::On_Parameter_Changed(pParameters, pParameter) );
181 }
182
183 //---------------------------------------------------------
On_Parameters_Enable(CSG_Parameters * pParameters,CSG_Parameter * pParameter)184 int CGrid_Calculator_Base::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
185 {
186 if( pParameter->Cmp_Identifier("XGRIDS") )
187 {
188 pParameters->Set_Enabled("RESAMPLING", pParameter->asList()->Get_Data_Count() > 0);
189 }
190
191 return( CSG_Tool_Grid::On_Parameters_Enable(pParameters, pParameter) );
192 }
193
194
195 ///////////////////////////////////////////////////////////
196 // //
197 ///////////////////////////////////////////////////////////
198
199 //---------------------------------------------------------
Initialize(int nGrids,int nXGrids)200 bool CGrid_Calculator_Base::Initialize(int nGrids, int nXGrids)
201 {
202 //-----------------------------------------------------
203 const int nVars = 27;
204
205 const SG_Char Vars[nVars] = SG_T("abcdefghijklmnopqrstuvwxyz");
206
207 //-----------------------------------------------------
208 CSG_String Formula(Parameters("FORMULA")->asString());
209
210 Formula.Replace("\n", "");
211 Formula.Replace("\t", "");
212
213 int nFuncs = 0;
214
215 if( (m_bPosition[0] = Formula.Find("col()" ) >= 0) ) { nFuncs++; }
216 if( (m_bPosition[1] = Formula.Find("row()" ) >= 0) ) { nFuncs++; }
217 if( (m_bPosition[2] = Formula.Find("xpos()" ) >= 0) ) { nFuncs++; }
218 if( (m_bPosition[3] = Formula.Find("ypos()" ) >= 0) ) { nFuncs++; }
219 if( (m_bPosition[4] = Formula.Find("ncols()") >= 0) ) { nFuncs++; }
220 if( (m_bPosition[5] = Formula.Find("nrows()") >= 0) ) { nFuncs++; }
221
222 //-----------------------------------------------------
223 m_nValues = nGrids + nXGrids + nFuncs;
224
225 if( m_nValues > nVars )
226 {
227 Error_Set(_TL("too many input variables"));
228
229 return( false );
230 }
231
232 //-----------------------------------------------------
233 int i, n = m_nValues;
234
235 if( m_bPosition[5] ) Formula.Replace("nrows()", Vars[--n]);
236 if( m_bPosition[4] ) Formula.Replace("ncols()", Vars[--n]);
237 if( m_bPosition[3] ) Formula.Replace("ypos()" , Vars[--n]);
238 if( m_bPosition[2] ) Formula.Replace("xpos()" , Vars[--n]);
239 if( m_bPosition[1] ) Formula.Replace("row()" , Vars[--n]);
240 if( m_bPosition[0] ) Formula.Replace("col()" , Vars[--n]);
241
242 for(i=nXGrids; i>0 && n>0; i--)
243 {
244 Formula.Replace(CSG_String::Format("h%d", i), Vars[--n]);
245 }
246
247 for(i= nGrids; i>0 && n>0; i--)
248 {
249 Formula.Replace(CSG_String::Format("g%d", i), Vars[--n]);
250 }
251
252 //-----------------------------------------------------
253 if( !m_Formula.Set_Formula(Formula) )
254 {
255 CSG_String Message;
256
257 if( !m_Formula.Get_Error(Message) )
258 {
259 Message.Printf("%s: %s", _TL("error in formula"), Formula.c_str());
260 }
261
262 Error_Set(Message);
263
264 return( false );
265 }
266
267 //-----------------------------------------------------
268 CSG_String Used(m_Formula.Get_Used_Variables());
269
270 int nUsed = (int)Used.Length() - nFuncs;
271
272 if( nGrids + nXGrids < nUsed )
273 {
274 Error_Fmt("%s (%d < %d)", _TL("The number of supplied grids is less than the number of variables in formula."),
275 nGrids + nXGrids, nUsed
276 );
277
278 return( false );
279 }
280
281 if( nGrids + nXGrids > nUsed )
282 {
283 Message_Fmt("\n%s: %s (%d > %d)", _TL("Warning"), _TL("The number of supplied grids exceeds the number of variables in formula."),
284 nGrids + nXGrids, nUsed
285 );
286 }
287
288 //-----------------------------------------------------
289 m_bUseNoData = Parameters("USE_NODATA")->asBool();
290
291 switch( Parameters("RESAMPLING")->asInt() )
292 {
293 default: m_Resampling = GRID_RESAMPLING_NearestNeighbour; break;
294 case 1: m_Resampling = GRID_RESAMPLING_Bilinear ; break;
295 case 2: m_Resampling = GRID_RESAMPLING_BicubicSpline ; break;
296 case 3: m_Resampling = GRID_RESAMPLING_BSpline ; break;
297 }
298
299 //-----------------------------------------------------
300 return( true );
301 }
302
303 //---------------------------------------------------------
Get_Result_Type(void)304 TSG_Data_Type CGrid_Calculator_Base::Get_Result_Type(void)
305 {
306 switch( Parameters("TYPE")->asInt() )
307 {
308 default: return( SG_DATATYPE_Float );
309 case 0: return( SG_DATATYPE_Bit );
310 case 1: return( SG_DATATYPE_Byte );
311 case 2: return( SG_DATATYPE_Char );
312 case 3: return( SG_DATATYPE_Word );
313 case 4: return( SG_DATATYPE_Short );
314 case 5: return( SG_DATATYPE_DWord );
315 case 6: return( SG_DATATYPE_Int );
316 case 7: return( SG_DATATYPE_Float );
317 case 8: return( SG_DATATYPE_Double );
318 }
319 }
320
321 //---------------------------------------------------------
Get_Result(const CSG_Vector & Values,double & Result)322 inline bool CGrid_Calculator_Base::Get_Result(const CSG_Vector &Values, double &Result)
323 {
324 return( _finite(Result = m_Formula.Get_Value(Values)) != 0 );
325 }
326
327
328 ///////////////////////////////////////////////////////////
329 // //
330 // //
331 // //
332 ///////////////////////////////////////////////////////////
333
334 //---------------------------------------------------------
CGrid_Calculator(void)335 CGrid_Calculator::CGrid_Calculator(void)
336 {
337 Set_Name (_TL("Grid Calculator"));
338
339 Set_Author ("O.Conrad (c) 2017, A.Ringeler (c) 2003");
340
341 CSG_String s(_TW(
342 "The Grid Calculator calculates a new grid based on existing grids and a mathematical formula. "
343 "The grid variables in the formula begin with the letter 'g' followed by a position index, "
344 "which corresponds to the order of the grids in the input grid list "
345 "(i.e.: g1, g2, g3, ... correspond to the first, second, third, ... grid in list). "
346 "Grids from other systems than the default one can be addressed likewise using the letter 'h' "
347 "(h1, h2, h3, ...), which correspond to the \'Grids from different Systems\' list.\n"
348 "\n"
349 "Example:\t sin(g1) * g2 + 2 * h1\n"
350 "\n"
351 "The following operators are available for the formula definition:\n"
352 ));
353
354 const CSG_String Operators[5][2] =
355 {
356 { "xpos(), ypos()" , _TL("Get the x/y coordinates for the current cell") },
357 { "col(), row()" , _TL("Get the current cell's column/row index" ) },
358 { "ncols(), nrows()", _TL("Get the number of columns/rows" ) },
359 { "nodata()" , _TL("Returns resulting grid's no-data value" ) },
360 { "", "" }
361 };
362
363 s += CSG_Formula::Get_Help_Operators(true, Operators);
364
365 Set_Description(s);
366
367 //-----------------------------------------------------
368 Parameters.Add_Grid_List("",
369 "GRIDS" , _TL("Grids"),
370 _TL("in the formula these grids are addressed in order of the list as 'g1, g2, g3, ...'"),
371 PARAMETER_INPUT_OPTIONAL
372 );
373
374 Parameters.Add_Grid_List("",
375 "XGRIDS" , _TL("Grids from different Systems"),
376 _TL("in the formula these grids are addressed in order of the list as 'h1, h2, h3, ...'"),
377 PARAMETER_INPUT_OPTIONAL, false
378 );
379
380 Parameters.Add_Grid("",
381 "RESULT" , _TL("Result"),
382 _TL(""),
383 PARAMETER_OUTPUT
384 );
385 }
386
387
388 ///////////////////////////////////////////////////////////
389 // //
390 ///////////////////////////////////////////////////////////
391
392 //---------------------------------------------------------
On_Execute(void)393 bool CGrid_Calculator::On_Execute(void)
394 {
395 //-----------------------------------------------------
396 m_pGrids = Parameters("GRIDS" )->asGridList();
397 m_pXGrids = Parameters("XGRIDS")->asGridList();
398
399 if( !Initialize(m_pGrids->Get_Grid_Count(), m_pXGrids->Get_Grid_Count()) )
400 {
401 return( false );
402 }
403
404 //-----------------------------------------------------
405 CSG_Grid *pResult = Parameters("RESULT")->asGrid();
406
407 if( pResult->Get_Type() != Get_Result_Type() )
408 {
409 pResult->Create(Get_System(), Get_Result_Type());
410 }
411
412 pResult->Set_Name(Parameters("NAME")->asString());
413
414 m_NoData_Value = pResult->Get_NoData_Value();
415
416 //-----------------------------------------------------
417 for(int y=0; y<Get_NY() && Set_Progress(y); y++)
418 {
419 #pragma omp parallel for
420 for(int x=0; x<Get_NX(); x++)
421 {
422 double Result; CSG_Vector Values(m_nValues);
423
424 if( Get_Values(x, y, Values) && Get_Result(Values, Result) )
425 {
426 pResult->Set_Value(x, y, Result);
427 }
428 else
429 {
430 pResult->Set_NoData(x, y);
431 }
432 }
433 }
434
435 //-----------------------------------------------------
436 return( true );
437 }
438
439
440 ///////////////////////////////////////////////////////////
441 // //
442 ///////////////////////////////////////////////////////////
443
444 //---------------------------------------------------------
Get_Values(int x,int y,CSG_Vector & Values)445 bool CGrid_Calculator::Get_Values(int x, int y, CSG_Vector &Values)
446 {
447 TSG_Point p = Get_System().Get_Grid_to_World(x, y);
448
449 if( m_pXGrids->Get_Grid_Count() > 0 )
450 {
451 for(int i=0, j=m_pGrids->Get_Grid_Count(); i<m_pXGrids->Get_Grid_Count(); i++, j++)
452 {
453 if( !m_pXGrids->Get_Grid(i)->Get_Value(p, Values[j], m_Resampling, m_bUseNoData) )
454 {
455 return( false );
456 }
457 }
458 }
459
460 for(int i=0; i<m_pGrids->Get_Grid_Count(); i++)
461 {
462 if( !m_bUseNoData && m_pGrids->Get_Grid(i)->is_NoData(x, y) )
463 {
464 return( false );
465 }
466
467 Values[i] = m_pGrids->Get_Grid(i)->asDouble(x, y);
468 }
469
470 int n = m_pGrids->Get_Grid_Count() + m_pXGrids->Get_Grid_Count();
471
472 if( m_bPosition[0] ) Values[n++] = x ; // col()
473 if( m_bPosition[1] ) Values[n++] = y ; // row()
474 if( m_bPosition[2] ) Values[n++] = p.x ; // xpos()
475 if( m_bPosition[3] ) Values[n++] = p.y ; // ypos()
476 if( m_bPosition[4] ) Values[n++] = Get_NX(); // ncols()
477 if( m_bPosition[5] ) Values[n++] = Get_NY(); // nrows()
478
479 return( true );
480 }
481
482
483 ///////////////////////////////////////////////////////////
484 // //
485 // //
486 // //
487 ///////////////////////////////////////////////////////////
488
489 //---------------------------------------------------------
CGrids_Calculator(void)490 CGrids_Calculator::CGrids_Calculator(void)
491 {
492 Set_Name (_TL("Grid Collection Calculator"));
493
494 Set_Author ("O.Conrad (c) 2018");
495
496 CSG_String s(_TW(
497 "The Grid Collection Calculator creates a new grid collection combining existing ones using the given formula. "
498 "It is assumed that all input grid collections have the same number of grid layers. "
499 "The variables in the formula begin with the letter 'g' followed by a position index, "
500 "which corresponds to the order of the grid collections in the input grid collection list "
501 "(i.e.: g1, g2, g3, ... correspond to the first, second, third, ... grid collection in list). "
502 "Grid collections from other systems than the default one can be addressed likewise using the letter 'h' "
503 "(h1, h2, h3, ...), which correspond to the \'Grid collections from different Systems\' list.\n"
504 "\n"
505 "Example:\t sin(g1) * g2 + 2 * h1\n"
506 "\n"
507 "The following operators are available for the formula definition:\n"
508 ));
509
510 const CSG_String Operators[5][2] =
511 {
512 { "xpos(), ypos()" , _TL("Get the x/y coordinates for the current cell") },
513 { "col(), row()" , _TL("Get the current cell's column/row index" ) },
514 { "ncols(), nrows()", _TL("Get the number of columns/rows" ) },
515 { "nodata()" , _TL("Returns resulting grid's no-data value" ) },
516 { "", "" }
517 };
518
519 s += CSG_Formula::Get_Help_Operators(true, Operators);
520
521 Set_Description(s);
522
523 //-----------------------------------------------------
524 Parameters.Add_Grids_List("",
525 "GRIDS" , _TL("Grid Collections"),
526 _TL("in the formula these grid collections are addressed in order of the list as 'g1, g2, g3, ...'"),
527 PARAMETER_INPUT
528 );
529
530 Parameters.Add_Grids_List("",
531 "XGRIDS" , _TL("Grid Collections from different Systems"),
532 _TL("in the formula these grid collections are addressed in order of the list as 'h1, h2, h3, ...'"),
533 PARAMETER_INPUT_OPTIONAL, false
534 );
535
536 Parameters.Add_Grids("",
537 "RESULT" , _TL("Result"),
538 _TL(""),
539 PARAMETER_OUTPUT
540 );
541 }
542
543
544 ///////////////////////////////////////////////////////////
545 // //
546 ///////////////////////////////////////////////////////////
547
548 //---------------------------------------------------------
On_Execute(void)549 bool CGrids_Calculator::On_Execute(void)
550 {
551 //-----------------------------------------------------
552 m_pGrids = Parameters("GRIDS" )->asGridsList();
553 m_pXGrids = Parameters("XGRIDS")->asGridsList();
554
555 int i, nz = m_pGrids->Get_Grids(0)->Get_NZ();
556
557 for(i=1; i<m_pGrids->Get_Item_Count(); i++)
558 {
559 CSG_Grids *pGrids = m_pGrids->Get_Grids(i);
560
561 if( pGrids->Get_NZ() != nz )
562 {
563 Error_Fmt("%s [%d, %s]", _TL("incompatible number of grid layers"), pGrids->Get_NZ(), pGrids->Get_Name());
564
565 return( false );
566 }
567 }
568
569 if( !Initialize(m_pGrids->Get_Item_Count(), m_pXGrids->Get_Item_Count()) )
570 {
571 return( false );
572 }
573
574 //-----------------------------------------------------
575 CSG_Grids *pResult = Parameters("RESULT")->asGrids();
576
577 if( pResult->Get_Type() != Get_Result_Type() || pResult->Get_NZ() != nz )
578 {
579 CSG_Grids *pGrids = m_pGrids->Get_Grids(0);
580
581 pResult->Create(Get_System(), pGrids->Get_Attributes(), pGrids->Get_Z_Attribute(), Get_Result_Type(), true);
582 }
583
584 pResult->Set_Name(Parameters("NAME")->asString());
585
586 m_NoData_Value = pResult->Get_NoData_Value();
587
588 //-----------------------------------------------------
589 for(int y=0; y<Get_NY() && Set_Progress(y); y++)
590 {
591 #pragma omp parallel for
592 for(int x=0; x<Get_NX(); x++)
593 {
594 double Result; CSG_Vector Values(m_nValues);
595
596 for(int z=0; z<pResult->Get_NZ(); z++)
597 {
598 if( Get_Values(x, y, z, Values) && Get_Result(Values, Result) )
599 {
600 pResult->Set_Value(x, y, z, Result);
601 }
602 else
603 {
604 pResult->Set_NoData(x, y, z);
605 }
606 }
607 }
608 }
609
610 //-----------------------------------------------------
611 return( true );
612 }
613
614
615 ///////////////////////////////////////////////////////////
616 // //
617 ///////////////////////////////////////////////////////////
618
619 //---------------------------------------------------------
Get_Values(int x,int y,int z,CSG_Vector & Values)620 bool CGrids_Calculator::Get_Values(int x, int y, int z, CSG_Vector &Values)
621 {
622 TSG_Point p = Get_System().Get_Grid_to_World(x, y);
623
624 if( m_pXGrids->Get_Item_Count() > 0 )
625 {
626 CSG_Grids *pGrids = m_pGrids->Get_Grids(0);
627
628 double pz = pGrids->Get_Z(z);
629
630 for(int i=0, j=m_pGrids->Get_Item_Count(); i<m_pXGrids->Get_Item_Count(); i++, j++)
631 {
632 if( !m_pXGrids->Get_Grids(i)->Get_Value(p.x, p.y, pz, Values[j], m_Resampling) )
633 {
634 return( false );
635 }
636 }
637 }
638
639 for(int i=0; i<m_pGrids->Get_Item_Count(); i++)
640 {
641 if( !m_bUseNoData && m_pGrids->Get_Grids(i)->is_NoData(x, y, z) )
642 {
643 return( false );
644 }
645
646 Values[i] = m_pGrids->Get_Grids(i)->asDouble(x, y, z);
647 }
648
649 int n = m_pGrids->Get_Item_Count() + m_pXGrids->Get_Item_Count();
650
651 if( m_bPosition[0] ) Values[n++] = x ; // col()
652 if( m_bPosition[1] ) Values[n++] = y ; // row()
653 if( m_bPosition[2] ) Values[n++] = p.x ; // xpos()
654 if( m_bPosition[3] ) Values[n++] = p.y ; // ypos()
655 if( m_bPosition[4] ) Values[n++] = Get_NX(); // ncols()
656 if( m_bPosition[5] ) Values[n++] = Get_NY(); // nrows()
657
658 return( true );
659 }
660
661
662 ///////////////////////////////////////////////////////////
663 // //
664 // //
665 // //
666 ///////////////////////////////////////////////////////////
667
668 //---------------------------------------------------------
669