Quantum GIS API Documentation  1.8
src/core/symbology-ng/qgssymbolv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgssymbolv2.cpp
00003     ---------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder.sk at gmail.com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgsrenderer.h"
00017 #include "qgssymbolv2.h"
00018 #include "qgssymbollayerv2.h"
00019 
00020 #include "qgslinesymbollayerv2.h"
00021 #include "qgsmarkersymbollayerv2.h"
00022 #include "qgsfillsymbollayerv2.h"
00023 
00024 #include "qgslogger.h"
00025 #include "qgsrendercontext.h" // for bigSymbolPreview
00026 
00027 #include <QColor>
00028 #include <QImage>
00029 #include <QPainter>
00030 #include <QSize>
00031 
00032 #include <cmath>
00033 
00034 QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
00035     : mType( type ), mLayers( layers ), mOutputUnit( MM ), mAlpha( 1.0 ), mRenderHints( 0 )
00036 {
00037 
00038   // check they're all correct symbol layers
00039   for ( int i = 0; i < mLayers.count(); i++ )
00040   {
00041     if ( mLayers[i] == NULL )
00042     {
00043       mLayers.removeAt( i-- );
00044     }
00045     else if ( !isSymbolLayerCompatible( mLayers[i]->type() ) )
00046     {
00047       delete mLayers[i];
00048       mLayers.removeAt( i-- );
00049     }
00050   }
00051 
00052 }
00053 
00054 QgsSymbolV2::~QgsSymbolV2()
00055 {
00056   // delete all symbol layers (we own them, so it's okay)
00057   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00058     delete *it;
00059 }
00060 
00061 QgsSymbolV2* QgsSymbolV2::defaultSymbol( QGis::GeometryType geomType )
00062 {
00063   QgsSymbolV2* s;
00064   switch ( geomType )
00065   {
00066     case QGis::Point: s = new QgsMarkerSymbolV2(); break;
00067     case QGis::Line:  s = new QgsLineSymbolV2(); break;
00068     case QGis::Polygon: s = new QgsFillSymbolV2(); break;
00069     default: QgsDebugMsg( "unknown layer's geometry type" ); return NULL;
00070   }
00071 
00072   s->setColor( QColor::fromHsv( rand() % 360, 64 + rand() % 192, 128 + rand() % 128 ) );
00073   return s;
00074 }
00075 
00076 
00077 QgsSymbolLayerV2* QgsSymbolV2::symbolLayer( int layer )
00078 {
00079   if ( layer < 0 || layer >= mLayers.count() )
00080     return NULL;
00081 
00082   return mLayers[layer];
00083 }
00084 
00085 
00086 bool QgsSymbolV2::isSymbolLayerCompatible( SymbolType t )
00087 {
00088   // fill symbol can contain also line symbol layers for drawing of outlines
00089   if ( mType == Fill && t == Line )
00090     return true;
00091 
00092   return mType == t;
00093 }
00094 
00095 
00096 bool QgsSymbolV2::insertSymbolLayer( int index, QgsSymbolLayerV2* layer )
00097 {
00098   if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
00099     return false;
00100   if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
00101     return false;
00102 
00103   mLayers.insert( index, layer );
00104   return true;
00105 }
00106 
00107 
00108 bool QgsSymbolV2::appendSymbolLayer( QgsSymbolLayerV2* layer )
00109 {
00110   if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
00111     return false;
00112 
00113   mLayers.append( layer );
00114   return true;
00115 }
00116 
00117 
00118 bool QgsSymbolV2::deleteSymbolLayer( int index )
00119 {
00120   if ( index < 0 || index >= mLayers.count() )
00121     return false;
00122 
00123   delete mLayers[index];
00124   mLayers.removeAt( index );
00125   return true;
00126 }
00127 
00128 
00129 QgsSymbolLayerV2* QgsSymbolV2::takeSymbolLayer( int index )
00130 {
00131   if ( index < 0 || index >= mLayers.count() )
00132     return NULL;
00133 
00134   return mLayers.takeAt( index );
00135 }
00136 
00137 
00138 bool QgsSymbolV2::changeSymbolLayer( int index, QgsSymbolLayerV2* layer )
00139 {
00140   if ( index < 0 || index >= mLayers.count() )
00141     return false;
00142   if ( layer == NULL || !isSymbolLayerCompatible( layer->type() ) )
00143     return false;
00144 
00145   delete mLayers[index]; // first delete the original layer
00146   mLayers[index] = layer; // set new layer
00147   return true;
00148 }
00149 
00150 
00151 void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsVectorLayer* layer )
00152 {
00153   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints );
00154   symbolContext.setLayer( layer );
00155   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00156     ( *it )->startRender( symbolContext );
00157 }
00158 
00159 void QgsSymbolV2::stopRender( QgsRenderContext& context )
00160 {
00161   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints );
00162   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00163     ( *it )->stopRender( symbolContext );
00164 }
00165 
00166 void QgsSymbolV2::setColor( const QColor& color )
00167 {
00168   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00169   {
00170     if ( !( *it )->isLocked() )
00171       ( *it )->setColor( color );
00172   }
00173 }
00174 
00175 QColor QgsSymbolV2::color()
00176 {
00177   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00178   {
00179     // return color of the first unlocked layer
00180     if ( !( *it )->isLocked() )
00181       return ( *it )->color();
00182   }
00183   return QColor( 0, 0, 0 );
00184 }
00185 
00186 void QgsSymbolV2::drawPreviewIcon( QPainter* painter, QSize size )
00187 {
00188   QgsRenderContext context = QgsSymbolLayerV2Utils::createRenderContext( painter );
00189   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, false, mRenderHints );
00190   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00191   {
00192     if ( mType == Fill && ( *it )->type() == Line )
00193     {
00194       // line symbol layer would normally draw just a line
00195       // so we override this case to force it to draw a polygon outline
00196       QgsLineSymbolLayerV2* lsl = ( QgsLineSymbolLayerV2* ) * it;
00197 
00198       // from QgsFillSymbolLayerV2::drawPreviewIcon()
00199       QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width() - 1, size.height() - 1 ) );
00200       lsl->startRender( symbolContext );
00201       lsl->renderPolygonOutline( poly, NULL, symbolContext );
00202       lsl->stopRender( symbolContext );
00203     }
00204     else
00205       ( *it )->drawPreviewIcon( symbolContext, size );
00206   }
00207 }
00208 
00209 
00210 QImage QgsSymbolV2::bigSymbolPreviewImage()
00211 {
00212   QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
00213   preview.fill( 0 );
00214 
00215   QPainter p( &preview );
00216   p.setRenderHint( QPainter::Antialiasing );
00217   p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialising
00218 
00219   if ( mType == QgsSymbolV2::Marker )
00220   {
00221     p.setPen( QPen( Qt::gray ) );
00222     p.drawLine( 0, 50, 100, 50 );
00223     p.drawLine( 50, 0, 50, 100 );
00224   }
00225 
00226   QgsRenderContext context = QgsSymbolLayerV2Utils::createRenderContext( &p );
00227   startRender( context );
00228 
00229   if ( mType == QgsSymbolV2::Line )
00230   {
00231     QPolygonF poly;
00232     poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
00233     static_cast<QgsLineSymbolV2*>( this )->renderPolyline( poly, 0, context );
00234   }
00235   else if ( mType == QgsSymbolV2::Fill )
00236   {
00237     QPolygonF polygon;
00238     polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
00239     static_cast<QgsFillSymbolV2*>( this )->renderPolygon( polygon, NULL, 0, context );
00240   }
00241   else // marker
00242   {
00243     static_cast<QgsMarkerSymbolV2*>( this )->renderPoint( QPointF( 50, 50 ), 0, context );
00244   }
00245 
00246   stopRender( context );
00247   return preview;
00248 }
00249 
00250 
00251 QString QgsSymbolV2::dump()
00252 {
00253   QString t;
00254   switch ( type() )
00255   {
00256     case QgsSymbolV2::Marker: t = "MARKER"; break;
00257     case QgsSymbolV2::Line: t = "LINE"; break;
00258     case QgsSymbolV2::Fill: t = "FILL"; break;
00259     default: Q_ASSERT( 0 && "unknown symbol type" );
00260   }
00261   QString s = QString( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerV2Utils::encodeColor( color() ) );
00262 
00263   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00264   {
00265     // TODO:
00266   }
00267   return s;
00268 }
00269 
00270 void QgsSymbolV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00271 {
00272   props[ "alpha" ] = QString::number( alpha() );
00273   double scaleFactor = 1.0;
00274   props[ "uom" ] = QgsSymbolLayerV2Utils::encodeSldUom( outputUnit(), &scaleFactor );
00275   props[ "uomScale" ] = scaleFactor != 1 ? QString::number( scaleFactor ) : "";
00276 
00277   for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00278   {
00279     ( *it )->toSld( doc, element, props );
00280   }
00281 }
00282 
00283 QgsSymbolLayerV2List QgsSymbolV2::cloneLayers() const
00284 {
00285   QgsSymbolLayerV2List lst;
00286   for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00287   {
00288     QgsSymbolLayerV2* layer = ( *it )->clone();
00289     layer->setLocked(( *it )->isLocked() );
00290     layer->setRenderingPass(( *it )->renderingPass() );
00291     lst.append( layer );
00292   }
00293   return lst;
00294 }
00295 
00296 QSet<QString> QgsSymbolV2::usedAttributes() const
00297 {
00298   QSet<QString> attributes;
00299   QgsSymbolLayerV2List::const_iterator sIt = mLayers.constBegin();
00300   for ( ; sIt != mLayers.constEnd(); ++sIt )
00301   {
00302     if ( *sIt )
00303     {
00304       attributes.unite(( *sIt )->usedAttributes() );
00305     }
00306   }
00307   return attributes;
00308 }
00309 
00311 
00312 QgsSymbolV2RenderContext::QgsSymbolV2RenderContext( QgsRenderContext& c, QgsSymbolV2::OutputUnit u, qreal alpha, bool selected, int renderHints, const QgsFeature* f )
00313     : mRenderContext( c ), mOutputUnit( u ), mAlpha( alpha ), mSelected( selected ), mRenderHints( renderHints ), mFeature( f ), mLayer( 0 )
00314 {
00315 
00316 }
00317 
00318 QgsSymbolV2RenderContext::~QgsSymbolV2RenderContext()
00319 {
00320 
00321 }
00322 
00323 QColor QgsSymbolV2RenderContext::selectionColor()
00324 {
00325   return QgsRenderer::selectionColor();
00326 }
00327 
00328 
00329 double QgsSymbolV2RenderContext::outputLineWidth( double width ) const
00330 {
00331   return width * QgsSymbolLayerV2Utils::lineWidthScaleFactor( mRenderContext, mOutputUnit );
00332 }
00333 
00334 double QgsSymbolV2RenderContext::outputPixelSize( double size ) const
00335 {
00336   return size * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( mRenderContext, mOutputUnit );
00337 }
00338 
00339 QgsSymbolV2RenderContext& QgsSymbolV2RenderContext::operator=( const QgsSymbolV2RenderContext& )
00340 {
00341   // This is just a dummy implementation of assignment.
00342   // sip 4.7 generates a piece of code that needs this function to exist.
00343   // It's not generated automatically by the compiler because of
00344   // mRenderContext member which is a reference (and thus can't be changed).
00345   Q_ASSERT( false );
00346   return *this;
00347 }
00348 
00350 
00351 QgsMarkerSymbolV2* QgsMarkerSymbolV2::createSimple( const QgsStringMap& properties )
00352 {
00353   QgsSymbolLayerV2* sl = QgsSimpleMarkerSymbolLayerV2::create( properties );
00354   if ( sl == NULL )
00355     return NULL;
00356 
00357   QgsSymbolLayerV2List layers;
00358   layers.append( sl );
00359   return new QgsMarkerSymbolV2( layers );
00360 }
00361 
00362 QgsLineSymbolV2* QgsLineSymbolV2::createSimple( const QgsStringMap& properties )
00363 {
00364   QgsSymbolLayerV2* sl = QgsSimpleLineSymbolLayerV2::create( properties );
00365   if ( sl == NULL )
00366     return NULL;
00367 
00368   QgsSymbolLayerV2List layers;
00369   layers.append( sl );
00370   return new QgsLineSymbolV2( layers );
00371 }
00372 
00373 QgsFillSymbolV2* QgsFillSymbolV2::createSimple( const QgsStringMap& properties )
00374 {
00375   QgsSymbolLayerV2* sl = QgsSimpleFillSymbolLayerV2::create( properties );
00376   if ( sl == NULL )
00377     return NULL;
00378 
00379   QgsSymbolLayerV2List layers;
00380   layers.append( sl );
00381   return new QgsFillSymbolV2( layers );
00382 }
00383 
00385 
00386 
00387 QgsMarkerSymbolV2::QgsMarkerSymbolV2( QgsSymbolLayerV2List layers )
00388     : QgsSymbolV2( Marker, layers )
00389 {
00390   if ( mLayers.count() == 0 )
00391     mLayers.append( new QgsSimpleMarkerSymbolLayerV2() );
00392 }
00393 
00394 void QgsMarkerSymbolV2::setAngle( double angle )
00395 {
00396   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00397   {
00398     QgsMarkerSymbolLayerV2* layer = ( QgsMarkerSymbolLayerV2* ) * it;
00399     layer->setAngle( angle );
00400   }
00401 }
00402 
00403 double QgsMarkerSymbolV2::angle()
00404 {
00405   QgsSymbolLayerV2List::const_iterator it = mLayers.begin();
00406 
00407   if ( it == mLayers.end() )
00408     return 0;
00409 
00410   // return angle of the first symbol layer
00411   const QgsMarkerSymbolLayerV2 *layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
00412   return layer->angle();
00413 }
00414 
00415 void QgsMarkerSymbolV2::setSize( double s )
00416 {
00417   double origSize = size();
00418 
00419   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00420   {
00421     QgsMarkerSymbolLayerV2* layer = static_cast<QgsMarkerSymbolLayerV2*>( *it );
00422     if ( layer->size() == origSize )
00423       layer->setSize( s );
00424     else
00425     {
00426       // proportionally scale size
00427       if ( origSize != 0 )
00428         layer->setSize( layer->size() * s / origSize );
00429     }
00430   }
00431 }
00432 
00433 double QgsMarkerSymbolV2::size()
00434 {
00435   // return size of the largest symbol
00436   double maxSize = 0;
00437   for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00438   {
00439     const QgsMarkerSymbolLayerV2* layer = static_cast<const QgsMarkerSymbolLayerV2 *>( *it );
00440     double lsize = layer->size();
00441     if ( lsize > maxSize )
00442       maxSize = lsize;
00443   }
00444   return maxSize;
00445 }
00446 
00447 void QgsMarkerSymbolV2::renderPoint( const QPointF& point, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
00448 {
00449   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f );
00450   if ( layer != -1 )
00451   {
00452     if ( layer >= 0 && layer < mLayers.count() )
00453       (( QgsMarkerSymbolLayerV2* ) mLayers[layer] )->renderPoint( point, symbolContext );
00454     return;
00455   }
00456 
00457   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00458   {
00459     QgsMarkerSymbolLayerV2* layer = ( QgsMarkerSymbolLayerV2* ) * it;
00460     layer->renderPoint( point, symbolContext );
00461   }
00462 }
00463 
00464 QgsSymbolV2* QgsMarkerSymbolV2::clone() const
00465 {
00466   QgsSymbolV2* cloneSymbol = new QgsMarkerSymbolV2( cloneLayers() );
00467   cloneSymbol->setOutputUnit( mOutputUnit );
00468   cloneSymbol->setAlpha( mAlpha );
00469   return cloneSymbol;
00470 }
00471 
00472 
00474 // LINE
00475 
00476 QgsLineSymbolV2::QgsLineSymbolV2( QgsSymbolLayerV2List layers )
00477     : QgsSymbolV2( Line, layers )
00478 {
00479   if ( mLayers.count() == 0 )
00480     mLayers.append( new QgsSimpleLineSymbolLayerV2() );
00481 }
00482 
00483 void QgsLineSymbolV2::setWidth( double w )
00484 {
00485   double origWidth = width();
00486 
00487   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00488   {
00489     QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
00490     if ( layer->width() == origWidth )
00491     {
00492       layer->setWidth( w );
00493     }
00494     else
00495     {
00496       // proportionally scale the width
00497       if ( origWidth != 0 )
00498         layer->setWidth( layer->width() * w / origWidth );
00499     }
00500   }
00501 }
00502 
00503 double QgsLineSymbolV2::width()
00504 {
00505   double maxWidth = 0;
00506   for ( QgsSymbolLayerV2List::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00507   {
00508     const QgsLineSymbolLayerV2* layer = ( const QgsLineSymbolLayerV2* ) * it;
00509     double width = layer->width();
00510     if ( width > maxWidth )
00511       maxWidth = width;
00512   }
00513   return maxWidth;
00514 }
00515 
00516 void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
00517 {
00518   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f );
00519   if ( layer != -1 )
00520   {
00521     if ( layer >= 0 && layer < mLayers.count() )
00522       (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolyline( points, symbolContext );
00523     return;
00524   }
00525 
00526   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00527   {
00528     QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
00529     layer->renderPolyline( points, symbolContext );
00530   }
00531 }
00532 
00533 
00534 QgsSymbolV2* QgsLineSymbolV2::clone() const
00535 {
00536   QgsSymbolV2* cloneSymbol = new QgsLineSymbolV2( cloneLayers() );
00537   cloneSymbol->setOutputUnit( mOutputUnit );
00538   cloneSymbol->setAlpha( mAlpha );
00539   return cloneSymbol;
00540 }
00541 
00543 // FILL
00544 
00545 QgsFillSymbolV2::QgsFillSymbolV2( QgsSymbolLayerV2List layers )
00546     : QgsSymbolV2( Fill, layers )
00547 {
00548   if ( mLayers.count() == 0 )
00549     mLayers.append( new QgsSimpleFillSymbolLayerV2() );
00550 }
00551 
00552 void QgsFillSymbolV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
00553 {
00554   QgsSymbolV2RenderContext symbolContext( context, mOutputUnit, mAlpha, selected, mRenderHints, f );
00555   if ( layer != -1 )
00556   {
00557     if ( layer >= 0 && layer < mLayers.count() )
00558     {
00559       QgsSymbolV2::SymbolType layertype = mLayers.at( layer )->type();
00560       if ( layertype == QgsSymbolV2::Fill )
00561         (( QgsFillSymbolLayerV2* ) mLayers[layer] )->renderPolygon( points, rings, symbolContext );
00562       else if ( layertype == QgsSymbolV2::Line )
00563         (( QgsLineSymbolLayerV2* ) mLayers[layer] )->renderPolygonOutline( points, rings, symbolContext );
00564     }
00565     return;
00566   }
00567 
00568   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00569   {
00570     QgsSymbolV2::SymbolType layertype = ( *it )->type();
00571     if ( layertype == QgsSymbolV2::Fill )
00572     {
00573       QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it;
00574       layer->renderPolygon( points, rings, symbolContext );
00575     }
00576     else if ( layertype == QgsSymbolV2::Line )
00577     {
00578       QgsLineSymbolLayerV2* layer = ( QgsLineSymbolLayerV2* ) * it;
00579       layer->renderPolygonOutline( points, rings, symbolContext );
00580     }
00581   }
00582 }
00583 
00584 
00585 QgsSymbolV2* QgsFillSymbolV2::clone() const
00586 {
00587   QgsSymbolV2* cloneSymbol = new QgsFillSymbolV2( cloneLayers() );
00588   cloneSymbol->setOutputUnit( mOutputUnit );
00589   cloneSymbol->setAlpha( mAlpha );
00590   return cloneSymbol;
00591 }
00592 
00593 void QgsFillSymbolV2::setAngle( double angle )
00594 {
00595   for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
00596   {
00597     QgsFillSymbolLayerV2* layer = ( QgsFillSymbolLayerV2* ) * it;
00598     layer->setAngle( angle );
00599   }
00600 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines