Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsrasterface.h - Internal raster processing modules interface
3 : : --------------------------------------
4 : : Date : Jun 21, 2012
5 : : Copyright : (C) 2012 by Radim Blazek
6 : : email : radim dot blazek at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #ifndef QGSRASTERINTERFACE_H
19 : : #define QGSRASTERINTERFACE_H
20 : :
21 : : #include "qgis_core.h"
22 : : #include "qgis_sip.h"
23 : : #include <limits>
24 : :
25 : : #include <QCoreApplication> // for tr()
26 : : #include <QImage>
27 : :
28 : : #include "qgsfeedback.h"
29 : : #include "qgsrasterbandstats.h"
30 : : #include "qgsrasterblock.h"
31 : : #include "qgsrasterhistogram.h"
32 : : #include "qgsrectangle.h"
33 : :
34 : : /**
35 : : * \ingroup core
36 : : * \brief Feedback object tailored for raster block reading.
37 : : *
38 : : * \since QGIS 3.0
39 : : */
40 : 0 : class CORE_EXPORT QgsRasterBlockFeedback : public QgsFeedback
41 : : {
42 : : Q_OBJECT
43 : :
44 : : public:
45 : : //! Construct a new raster block feedback object
46 : 0 : QgsRasterBlockFeedback( QObject *parent = nullptr ) : QgsFeedback( parent ) {}
47 : :
48 : : /**
49 : : * May be emitted by raster data provider to indicate that some partial data are available
50 : : * and a new preview image may be produced
51 : : */
52 : : virtual void onNewData() {}
53 : :
54 : : /**
55 : : * Whether the raster provider should return only data that are already available
56 : : * without waiting for full result. By default this flag is not enabled.
57 : : * \see setPreviewOnly()
58 : : */
59 : : bool isPreviewOnly() const { return mPreviewOnly; }
60 : :
61 : : /**
62 : : * set flag whether the block request is for preview purposes only
63 : : * \see isPreviewOnly()
64 : : */
65 : 0 : void setPreviewOnly( bool preview ) { mPreviewOnly = preview; }
66 : :
67 : : /**
68 : : * Whether our painter is drawing to a temporary image used just by this layer
69 : : * \see setRenderPartialOutput()
70 : : */
71 : 0 : bool renderPartialOutput() const { return mRenderPartialOutput; }
72 : :
73 : : /**
74 : : * Set whether our painter is drawing to a temporary image used just by this layer
75 : : * \see renderPartialOutput()
76 : : */
77 : 0 : void setRenderPartialOutput( bool enable ) { mRenderPartialOutput = enable; }
78 : :
79 : : /**
80 : : * Appends an error message to the stored list of errors. Should be called
81 : : * whenever an error is encountered while retrieving a raster block.
82 : : *
83 : : * \see errors()
84 : : * \since QGIS 3.8.0
85 : : */
86 : 0 : void appendError( const QString &error ) { mErrors.append( error ); }
87 : :
88 : : /**
89 : : * Returns a list of any errors encountered while retrieving the raster block.
90 : : *
91 : : * \see appendError()
92 : : * \since QGIS 3.8.0
93 : : */
94 : 0 : QStringList errors() const { return mErrors; }
95 : :
96 : : private:
97 : :
98 : : /**
99 : : * Whether the raster provider should return only data that are already available
100 : : * without waiting for full result
101 : : */
102 : 0 : bool mPreviewOnly = false;
103 : :
104 : : //! Whether our painter is drawing to a temporary image used just by this layer
105 : 0 : bool mRenderPartialOutput = false;
106 : :
107 : : //! List of errors encountered while retrieving block
108 : : QStringList mErrors;
109 : : };
110 : :
111 : :
112 : : /**
113 : : * \ingroup core
114 : : * \brief Base class for processing filters like renderers, reprojector, resampler etc.
115 : : */
116 : : class CORE_EXPORT QgsRasterInterface
117 : : {
118 : : #ifdef SIP_RUN
119 : : // QgsRasterInterface subclasses
120 : : #include <qgsbrightnesscontrastfilter.h>
121 : : #include <qgshuesaturationfilter.h>
122 : : #include <qgsrasterdataprovider.h>
123 : : #include <qgsrasternuller.h>
124 : : #include <qgsrasterprojector.h>
125 : : #include <qgsrasterrenderer.h>
126 : : #include <qgsrasterresamplefilter.h>
127 : :
128 : : // QgsRasterRenderer subclasses
129 : : #include <qgshillshaderenderer.h>
130 : : #include <qgsmultibandcolorrenderer.h>
131 : : #include <qgspalettedrasterrenderer.h>
132 : : #include <qgssinglebandcolordatarenderer.h>
133 : : #include <qgssinglebandgrayrenderer.h>
134 : : #include <qgssinglebandpseudocolorrenderer.h>
135 : : #endif
136 : :
137 : :
138 : : #ifdef SIP_RUN
139 : : SIP_CONVERT_TO_SUBCLASS_CODE
140 : : if ( dynamic_cast<QgsBrightnessContrastFilter *>( sipCpp ) )
141 : : sipType = sipType_QgsBrightnessContrastFilter;
142 : : else if ( dynamic_cast<QgsHueSaturationFilter *>( sipCpp ) )
143 : : sipType = sipType_QgsHueSaturationFilter;
144 : : else if ( dynamic_cast<QgsRasterDataProvider *>( sipCpp ) )
145 : : {
146 : : sipType = sipType_QgsRasterDataProvider;
147 : : // use static cast because QgsRasterDataProvider has multiple inheritance
148 : : // and we would end up with bad pointer otherwise!
149 : : *sipCppRet = static_cast<QgsRasterDataProvider *>( sipCpp );
150 : : }
151 : : else if ( dynamic_cast<QgsRasterNuller *>( sipCpp ) )
152 : : sipType = sipType_QgsRasterNuller;
153 : : else if ( dynamic_cast<QgsRasterProjector *>( sipCpp ) )
154 : : sipType = sipType_QgsRasterProjector;
155 : : else if ( dynamic_cast<QgsRasterRenderer *>( sipCpp ) )
156 : : {
157 : : if ( dynamic_cast<QgsHillshadeRenderer *>( sipCpp ) )
158 : : sipType = sipType_QgsHillshadeRenderer;
159 : : else if ( dynamic_cast<QgsMultiBandColorRenderer *>( sipCpp ) )
160 : : sipType = sipType_QgsMultiBandColorRenderer;
161 : : else if ( dynamic_cast<QgsPalettedRasterRenderer *>( sipCpp ) )
162 : : sipType = sipType_QgsPalettedRasterRenderer;
163 : : else if ( dynamic_cast<QgsSingleBandColorDataRenderer *>( sipCpp ) )
164 : : sipType = sipType_QgsSingleBandColorDataRenderer;
165 : : else if ( dynamic_cast<QgsSingleBandGrayRenderer *>( sipCpp ) )
166 : : sipType = sipType_QgsSingleBandGrayRenderer;
167 : : else if ( dynamic_cast<QgsSingleBandPseudoColorRenderer *>( sipCpp ) )
168 : : sipType = sipType_QgsSingleBandPseudoColorRenderer;
169 : : else
170 : : sipType = sipType_QgsRasterRenderer;
171 : : }
172 : : else if ( dynamic_cast<QgsRasterResampleFilter *>( sipCpp ) )
173 : : sipType = sipType_QgsRasterResampleFilter;
174 : : else
175 : : sipType = 0;
176 : : SIP_END
177 : : #endif
178 : :
179 : 0 : Q_DECLARE_TR_FUNCTIONS( QgsRasterInterface )
180 : :
181 : : public:
182 : : //! If you add to this, please also add to capabilitiesString()
183 : : enum Capability
184 : : {
185 : : NoCapabilities = 0,
186 : : Size = 1 << 1, //!< Original data source size (and thus resolution) is known, it is not always available, for example for WMS
187 : : Create = 1 << 2, //!< Create new datasets
188 : : Remove = 1 << 3, //!< Delete datasets
189 : : BuildPyramids = 1 << 4, //!< Supports building of pyramids (overviews)
190 : : Identify = 1 << 5, //!< At least one identify format supported
191 : : IdentifyValue = 1 << 6, //!< Numerical values
192 : : IdentifyText = 1 << 7, //!< WMS text
193 : : IdentifyHtml = 1 << 8, //!< WMS HTML
194 : : IdentifyFeature = 1 << 9, //!< WMS GML -> feature
195 : : Prefetch = 1 << 10, //!< Allow prefetching of out-of-view images
196 : : };
197 : :
198 : : QgsRasterInterface( QgsRasterInterface *input = nullptr );
199 : :
200 : 0 : virtual ~QgsRasterInterface() = default;
201 : :
202 : : //! Clone itself, create deep copy
203 : : virtual QgsRasterInterface *clone() const = 0 SIP_FACTORY;
204 : :
205 : : //! Returns a bitmask containing the supported capabilities
206 : 0 : virtual int capabilities() const
207 : : {
208 : 0 : return QgsRasterInterface::NoCapabilities;
209 : : }
210 : :
211 : : /**
212 : : * Returns the raster interface capabilities in friendly format.
213 : : */
214 : : QString capabilitiesString() const;
215 : :
216 : : //! Returns data type for the band specified by number
217 : : virtual Qgis::DataType dataType( int bandNo ) const = 0;
218 : :
219 : : /**
220 : : * Returns source data type for the band specified by number,
221 : : * source data type may be shorter than dataType
222 : : */
223 : 0 : virtual Qgis::DataType sourceDataType( int bandNo ) const { return mInput ? mInput->sourceDataType( bandNo ) : Qgis::UnknownDataType; }
224 : :
225 : : /**
226 : : * Gets the extent of the interface.
227 : : * \returns QgsRectangle containing the extent of the layer
228 : : */
229 : 0 : virtual QgsRectangle extent() const { return mInput ? mInput->extent() : QgsRectangle(); }
230 : :
231 : 0 : int dataTypeSize( int bandNo ) { return QgsRasterBlock::typeSize( dataType( bandNo ) ); }
232 : :
233 : : //! Gets number of bands
234 : : virtual int bandCount() const = 0;
235 : :
236 : : //! Gets block size
237 : 0 : virtual int xBlockSize() const { return mInput ? mInput->xBlockSize() : 0; }
238 : 0 : virtual int yBlockSize() const { return mInput ? mInput->yBlockSize() : 0; }
239 : :
240 : : //! Gets raster size
241 : 0 : virtual int xSize() const { return mInput ? mInput->xSize() : 0; }
242 : 0 : virtual int ySize() const { return mInput ? mInput->ySize() : 0; }
243 : :
244 : : //! \brief helper function to create zero padded band names
245 : : virtual QString generateBandName( int bandNumber ) const;
246 : :
247 : : /**
248 : : * Returns the name of the color interpretation for the specified \a bandNumber.
249 : : *
250 : : * \since QGIS 3.18
251 : : */
252 : : virtual QString colorInterpretationName( int bandNumber ) const;
253 : :
254 : : /**
255 : : * Generates a friendly, descriptive name for the specified \a bandNumber.
256 : : *
257 : : * \since QGIS 3.18
258 : : */
259 : : QString displayBandName( int bandNumber ) const;
260 : :
261 : : /**
262 : : * Read block of data using given extent and size.
263 : : * Returns pointer to data.
264 : : * Caller is responsible to free the memory returned.
265 : : * \param bandNo band number
266 : : * \param extent extent of block
267 : : * \param width pixel width of block
268 : : * \param height pixel height of block
269 : : * \param feedback optional raster feedback object for cancellation/preview. Added in QGIS 3.0.
270 : : */
271 : : virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = nullptr ) = 0 SIP_FACTORY;
272 : :
273 : : /**
274 : : * Set input.
275 : : * Returns TRUE if set correctly, FALSE if cannot use that input
276 : : */
277 : 0 : virtual bool setInput( QgsRasterInterface *input ) { mInput = input; return true; }
278 : :
279 : : //! Current input
280 : 0 : virtual QgsRasterInterface *input() const { return mInput; }
281 : :
282 : : //! Returns whether the interface is on or off
283 : 0 : virtual bool on() const { return mOn; }
284 : :
285 : : //! Sets whether the interface is on or off
286 : 0 : virtual void setOn( bool on ) { mOn = on; }
287 : :
288 : : /**
289 : : * Gets source / raw input, the first in pipe, usually provider.
290 : : * It may be used to get info about original data, e.g. resolution to decide
291 : : * resampling etc.
292 : : * \note not available in Python bindings.
293 : : */
294 : 0 : virtual const QgsRasterInterface *sourceInput() const SIP_SKIP
295 : : {
296 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
297 : 0 : return mInput ? mInput->sourceInput() : this;
298 : : }
299 : :
300 : : /**
301 : : * Gets source / raw input, the first in pipe, usually provider.
302 : : * It may be used to get info about original data, e.g. resolution to decide
303 : : * resampling etc.
304 : : */
305 : 0 : virtual QgsRasterInterface *sourceInput()
306 : : {
307 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
308 : 0 : return mInput ? mInput->sourceInput() : this;
309 : : }
310 : :
311 : : /**
312 : : * Returns the band statistics.
313 : : * \param bandNo The band (number).
314 : : * \param stats Requested statistics
315 : : * \param extent Extent used to calc statistics, if empty, whole raster extent is used.
316 : : * \param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
317 : : * \param feedback optional feedback object
318 : : */
319 : : virtual QgsRasterBandStats bandStatistics( int bandNo,
320 : : int stats = QgsRasterBandStats::All,
321 : : const QgsRectangle &extent = QgsRectangle(),
322 : : int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr );
323 : :
324 : : /**
325 : : * \brief Returns TRUE if histogram is available (cached, already calculated). * The parameters are the same as in bandStatistics()
326 : : * \returns TRUE if statistics are available (ready to use)
327 : : */
328 : : virtual bool hasStatistics( int bandNo,
329 : : int stats = QgsRasterBandStats::All,
330 : : const QgsRectangle &extent = QgsRectangle(),
331 : : int sampleSize = 0 );
332 : :
333 : :
334 : : /**
335 : : * Returns a band histogram. Histograms are cached in providers.
336 : : * \param bandNo The band (number).
337 : : * \param binCount Number of bins (intervals,buckets). If 0, the number of bins is decided automatically according to data type, raster size etc.
338 : : * \param minimum Minimum value, if NaN (None for Python), raster minimum value will be used.
339 : : * \param maximum Maximum value, if NaN (None for Python), raster maximum value will be used.
340 : : * \param extent Extent used to calc histogram, if empty, whole raster extent is used.
341 : : * \param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
342 : : * \param includeOutOfRange include out of range values
343 : : * \param feedback optional feedback object
344 : : * \returns Vector of non NULL cell counts for each bin.
345 : : * \note binCount, minimum and maximum not optional in Python bindings
346 : : */
347 : : #ifndef SIP_RUN
348 : : virtual QgsRasterHistogram histogram( int bandNo,
349 : : int binCount = 0,
350 : : double minimum = std::numeric_limits<double>::quiet_NaN(),
351 : : double maximum = std::numeric_limits<double>::quiet_NaN(),
352 : : const QgsRectangle &extent = QgsRectangle(),
353 : : int sampleSize = 0,
354 : : bool includeOutOfRange = false,
355 : : QgsRasterBlockFeedback *feedback = nullptr );
356 : : #else
357 : : virtual QgsRasterHistogram histogram( int bandNo,
358 : : int binCount = 0,
359 : : SIP_PYOBJECT minimum = Py_None,
360 : : SIP_PYOBJECT maximum = Py_None,
361 : : const QgsRectangle &extent = QgsRectangle(),
362 : : int sampleSize = 0,
363 : : bool includeOutOfRange = false,
364 : : QgsRasterBlockFeedback *feedback = nullptr )
365 : : [QgsRasterHistogram( int bandNo,
366 : : int binCount = 0,
367 : : double minimum = 0.0,
368 : : double maximum = 0.0,
369 : : const QgsRectangle &extent = QgsRectangle(),
370 : : int sampleSize = 0,
371 : : bool includeOutOfRange = false,
372 : : QgsRasterBlockFeedback *feedback = nullptr )];
373 : : % MethodCode
374 : : double minimum;
375 : : double maximum;
376 : : if ( a2 == Py_None )
377 : : {
378 : : minimum = std::numeric_limits<double>::quiet_NaN();
379 : : }
380 : : else
381 : : {
382 : : minimum = PyFloat_AsDouble( a2 );
383 : : }
384 : :
385 : : if ( a3 == Py_None )
386 : : {
387 : : maximum = std::numeric_limits<double>::quiet_NaN();
388 : : }
389 : : else
390 : : {
391 : : maximum = PyFloat_AsDouble( a3 );
392 : : }
393 : :
394 : : QgsRasterHistogram *h = new QgsRasterHistogram( sipCpp->histogram( a0, a1, minimum, maximum, *a4, a5, a6, a7 ) );
395 : : return sipConvertFromType( h, sipType_QgsRasterHistogram, Py_None );
396 : : % End
397 : : #endif
398 : :
399 : :
400 : : /**
401 : : * \brief Returns TRUE if histogram is available (cached, already calculated)
402 : : * \note the parameters are the same as in \see histogram()
403 : : */
404 : : #ifndef SIP_RUN
405 : : virtual bool hasHistogram( int bandNo,
406 : : int binCount,
407 : : double minimum = std::numeric_limits<double>::quiet_NaN(),
408 : : double maximum = std::numeric_limits<double>::quiet_NaN(),
409 : : const QgsRectangle &extent = QgsRectangle(),
410 : : int sampleSize = 0,
411 : : bool includeOutOfRange = false );
412 : : #else
413 : : virtual bool hasHistogram( int bandNo,
414 : : int binCount,
415 : : SIP_PYOBJECT minimum = Py_None,
416 : : SIP_PYOBJECT maximum = Py_None,
417 : : const QgsRectangle &extent = QgsRectangle(),
418 : : int sampleSize = 0,
419 : : bool includeOutOfRange = false )
420 : : [bool( int bandNo,
421 : : int binCount,
422 : : double minimum = 0.0,
423 : : double maximum = 0.0,
424 : : const QgsRectangle &extent = QgsRectangle(),
425 : : int sampleSize = 0,
426 : : bool includeOutOfRange = false )];
427 : : % MethodCode
428 : : double minimum;
429 : : double maximum;
430 : : if ( a2 == Py_None )
431 : : {
432 : : minimum = std::numeric_limits<double>::quiet_NaN();
433 : : }
434 : : else
435 : : {
436 : : minimum = PyFloat_AsDouble( a2 );
437 : : }
438 : :
439 : : if ( a3 == Py_None )
440 : : {
441 : : maximum = std::numeric_limits<double>::quiet_NaN();
442 : : }
443 : : else
444 : : {
445 : : maximum = PyFloat_AsDouble( a3 );
446 : : }
447 : :
448 : : sipRes = sipCpp->hasHistogram( a0, a1, minimum, maximum, *a4, a5, a6 );
449 : : % End
450 : : #endif
451 : :
452 : :
453 : : /**
454 : : * \brief Find values for cumulative pixel count cut.
455 : : * \param bandNo The band (number).
456 : : * \param lowerCount The lower count as fraction of 1, e.g. 0.02 = 2%
457 : : * \param upperCount The upper count as fraction of 1, e.g. 0.98 = 98%
458 : : * \param lowerValue Location into which the lower value will be set.
459 : : * \param upperValue Location into which the upper value will be set.
460 : : * \param extent Extent used to calc histogram, if empty, whole raster extent is used.
461 : : * \param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
462 : : */
463 : : virtual void cumulativeCut( int bandNo,
464 : : double lowerCount,
465 : : double upperCount,
466 : : double &lowerValue,
467 : : double &upperValue,
468 : : const QgsRectangle &extent = QgsRectangle(),
469 : : int sampleSize = 0 );
470 : :
471 : : //! Write base class members to xml.
472 : 0 : virtual void writeXml( QDomDocument &doc, QDomElement &parentElem ) const { Q_UNUSED( doc ) Q_UNUSED( parentElem ); }
473 : : //! Sets base class members from xml. Usually called from create() methods of subclasses
474 : 0 : virtual void readXml( const QDomElement &filterElem ) { Q_UNUSED( filterElem ) }
475 : :
476 : : protected:
477 : : // QgsRasterInterface used as input
478 : : QgsRasterInterface *mInput = nullptr;
479 : :
480 : : //! \brief List of cached statistics, all bands mixed
481 : : QList<QgsRasterBandStats> mStatistics;
482 : :
483 : : //! \brief List of cached histograms, all bands mixed
484 : : QList<QgsRasterHistogram> mHistograms;
485 : :
486 : : // On/off state, if off, it does not do anything, replicates input
487 : : bool mOn = true;
488 : :
489 : : /**
490 : : * Fill in histogram defaults if not specified
491 : : * \note the parameters are the same as in \see histogram()
492 : : */
493 : : #ifndef SIP_RUN
494 : : void initHistogram( QgsRasterHistogram &histogram,
495 : : int bandNo,
496 : : int binCount,
497 : : double minimum = std::numeric_limits<double>::quiet_NaN(),
498 : : double maximum = std::numeric_limits<double>::quiet_NaN(),
499 : : const QgsRectangle &boundingBox = QgsRectangle(),
500 : : int sampleSize = 0,
501 : : bool includeOutOfRange = false );
502 : : #else
503 : : void initHistogram( QgsRasterHistogram &histogram,
504 : : int bandNo,
505 : : int binCount,
506 : : SIP_PYOBJECT minimum = Py_None,
507 : : SIP_PYOBJECT maximum = Py_None,
508 : : const QgsRectangle &boundingBox = QgsRectangle(),
509 : : int sampleSize = 0,
510 : : bool includeOutOfRange = false )
511 : : [void ( QgsRasterHistogram & histogram,
512 : : int bandNo,
513 : : int binCount,
514 : : double minimum = 0.0,
515 : : double maximum = 0.0,
516 : : const QgsRectangle &boundingBox = QgsRectangle(),
517 : : int sampleSize = 0,
518 : : bool includeOutOfRange = false )];
519 : : % MethodCode
520 : : double minimum;
521 : : double maximum;
522 : : if ( a3 == Py_None )
523 : : {
524 : : minimum = std::numeric_limits<double>::quiet_NaN();
525 : : }
526 : : else
527 : : {
528 : : minimum = PyFloat_AsDouble( a3 );
529 : : }
530 : :
531 : : if ( a4 == Py_None )
532 : : {
533 : : maximum = std::numeric_limits<double>::quiet_NaN();
534 : : }
535 : : else
536 : : {
537 : : maximum = PyFloat_AsDouble( a4 );
538 : : }
539 : :
540 : : #if defined(SIP_PROTECTED_IS_PUBLIC)
541 : : sipCpp->initHistogram( *a0, a1, a2, minimum, maximum, *a5, a6, a7 );
542 : : #else
543 : : sipCpp->sipProtect_initHistogram( *a0, a1, a2, minimum, maximum, *a5, a6, a7 );
544 : : #endif
545 : : % End
546 : : #endif
547 : :
548 : :
549 : :
550 : : //! Fill in statistics defaults if not specified
551 : : void initStatistics( QgsRasterBandStats &statistics, int bandNo,
552 : : int stats = QgsRasterBandStats::All,
553 : : const QgsRectangle &boundingBox = QgsRectangle(),
554 : : int binCount = 0 );
555 : :
556 : : private:
557 : : #ifdef SIP_RUN
558 : : QgsRasterInterface( const QgsRasterInterface & );
559 : : QgsRasterInterface &operator=( const QgsRasterInterface & );
560 : : #endif
561 : :
562 : : Q_DISABLE_COPY( QgsRasterInterface ) // there is clone() for copying
563 : : };
564 : :
565 : : #endif
566 : :
567 : :
|