From e3d30af7358887616c2bc6616ae2e29facee7c79 Mon Sep 17 00:00:00 2001
From: Larry Shaffer <larrys@dakotacarto.com>
Date: Wed, 14 Nov 2012 18:31:27 -0700
Subject: [PATCH] Update to limit of number of features sent to PAL

- Distributes max number across total number of features registered in PAL
- Caveat: PAL may not show label for every registered feature
---
 python/core/qgsmaprenderer.sip |  3 +++
 python/core/qgspallabeling.sip |  2 --
 src/core/qgsmaprenderer.h      |  4 ++++
 src/core/qgspallabeling.cpp    | 44 +++++++++++++++++++++++++++-------
 src/core/qgspallabeling.h      |  7 +++---
 src/core/qgsvectorlayer.cpp    | 15 ++++++++++++
 src/ui/qgslabelingguibase.ui   |  7 ++++--
 7 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/python/core/qgsmaprenderer.sip b/python/core/qgsmaprenderer.sip
index 578c4198c8f..2cd7e31c470 100644
--- a/python/core/qgsmaprenderer.sip
+++ b/python/core/qgsmaprenderer.sip
@@ -40,6 +40,9 @@ class QgsLabelingEngineInterface
     //! called when starting rendering of a layer
     //! @note: this method was added in version 1.6
     virtual int prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx ) = 0;
+    //! returns PAL layer settings for a registered layer
+    //! @note: this method was added in version 1.9
+    virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0;
     //! adds a diagram layer to the labeling engine
     virtual int addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSettings* s );
     //! called for every feature
diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip
index bb785b729a1..9a6130e9d64 100644
--- a/python/core/qgspallabeling.sip
+++ b/python/core/qgspallabeling.sip
@@ -126,8 +126,6 @@ class QgsPalLayerSettings
     double minFeatureSize; // minimum feature size to be labelled (in mm)
     bool limitNumLabels; // whether to limit the number of labels to be drawn
     int maxNumLabels; // maximum number of labels to be drawn
-    //bool rndMaxNumLabels; // whether to take a randomized maxNumLabels subset of features to be labeled
-
     // Adds '<' or '>', or user-defined symbol to the label string pointing to the
     // direction of the line / polygon ring
     // Works only if Placement == Line
diff --git a/src/core/qgsmaprenderer.h b/src/core/qgsmaprenderer.h
index df75b919392..792b0c2b7fd 100644
--- a/src/core/qgsmaprenderer.h
+++ b/src/core/qgsmaprenderer.h
@@ -39,6 +39,7 @@ class QgsDistanceArea;
 class QgsOverlayObjectPositionManager;
 class QgsVectorLayer;
 
+class QgsPalLayerSettings;
 class QgsDiagramLayerSettings;
 
 class CORE_EXPORT QgsLabelPosition
@@ -75,6 +76,9 @@ class CORE_EXPORT QgsLabelingEngineInterface
     //! called when starting rendering of a layer
     //! @note: this method was added in version 1.6
     virtual int prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx ) = 0;
+    //! returns PAL layer settings for a registered layer
+    //! @note: this method was added in version 1.9
+    virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0;
     //! adds a diagram layer to the labeling engine
     virtual int addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSettings* s )
     { Q_UNUSED( layer ); Q_UNUSED( s ); return 0; }
diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp
index 76194f19884..e2e2e685d17 100644
--- a/src/core/qgspallabeling.cpp
+++ b/src/core/qgspallabeling.cpp
@@ -177,7 +177,7 @@ class QgsPalGeometry : public PalGeometry
 // -------------
 
 QgsPalLayerSettings::QgsPalLayerSettings()
-    : palLayer( NULL ), ct( NULL ), extentGeom( NULL ), expression( NULL )
+    : palLayer( NULL ), ct( NULL ), extentGeom( NULL ), mFeaturesToLabel( 0 ), mFeatsSendingToPal( 0 ), mFeatsRegPal( 0 ), expression( NULL )
 {
   placement = AroundPoint;
   placementFlags = 0;
@@ -658,13 +658,6 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
 
 void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer,  QgsFeature& f, const QgsRenderContext& context )
 {
-
-  // check if max number of labels to draw (already registered features) has been reached
-  if ( limitNumLabels && ( maxNumLabels == 0 || palLayer->getNbFeatures() >= maxNumLabels ) )
-  {
-    return;
-  }
-
   // data defined show label? defaults to show label if not 0
   QMap< DataDefinedProperties, int >::const_iterator showIt = dataDefinedProperties.find( QgsPalLayerSettings::Show );
   if ( showIt != dataDefinedProperties.constEnd() )
@@ -822,6 +815,32 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer,  QgsFeature& f
   if ( geos_geom == NULL )
     return; // invalid geometry
 
+  // likelihood exists label will be registered with PAL and may be drawn
+  // check if max number of features to label (already registered with PAL) has been reached
+  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
+  if ( limitNumLabels )
+  {
+    if ( !maxNumLabels )
+    {
+      return;
+    }
+    mFeatsRegPal = palLayer->getNbFeatures();
+    if ( mFeatsRegPal >= maxNumLabels )
+    {
+      return;
+    }
+
+    int divNum = ( int )(( mFeaturesToLabel / maxNumLabels ) + 0.5 );
+    if ( divNum && ( mFeatsRegPal == ( int )( mFeatsSendingToPal / divNum ) ) )
+    {
+      mFeatsSendingToPal += 1;
+      if ( divNum &&  mFeatsSendingToPal % divNum )
+      {
+        return;
+      }
+    }
+  }
+
   GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom );
 
   //data defined position / alignment / rotation?
@@ -1259,6 +1278,8 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices,
   // rect for clipping
   lyr.extentGeom = QgsGeometry::fromRect( mMapRenderer->extent() );
 
+  lyr.mFeatsSendingToPal = 0;
+
   return 1; // init successful
 }
 
@@ -1656,6 +1677,13 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
     QgsPalLayerSettings& lyr = lit.value();
     for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
       delete *git;
+    if ( lyr.limitNumLabels )
+    {
+      QgsDebugMsg( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ) );
+      QgsDebugMsg( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ) );
+      QgsDebugMsg( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ) );
+      QgsDebugMsg( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ) );
+    }
     lyr.geometries.clear();
   }
 
diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h
index 02e973f79fd..4081a1d9568 100644
--- a/src/core/qgspallabeling.h
+++ b/src/core/qgspallabeling.h
@@ -180,8 +180,6 @@ class CORE_EXPORT QgsPalLayerSettings
     double minFeatureSize; // minimum feature size to be labelled (in mm)
     bool limitNumLabels; // whether to limit the number of labels to be drawn
     int maxNumLabels; // maximum number of labels to be drawn
-    //bool rndMaxNumLabels; // whether to take a randomized maxNumLabels subset of features to be labeled
-
     // Adds '<' or '>', or user-defined symbol to the label string pointing to the
     // direction of the line / polygon ring
     // Works only if Placement == Line
@@ -228,7 +226,7 @@ class CORE_EXPORT QgsPalLayerSettings
      @return font pixel size*/
     int sizeToPixel( double size, const QgsRenderContext& c , bool buffer = false ) const;
 
-    // temporary stuff: set when layer gets prepared
+    // temporary stuff: set when layer gets prepared or labeled
     pal::Layer* palLayer;
     int fieldIndex;
     const QgsMapToPixel* xform;
@@ -236,6 +234,9 @@ class CORE_EXPORT QgsPalLayerSettings
     QgsPoint ptZero, ptOne;
     QList<QgsPalGeometry*> geometries;
     QgsGeometry* extentGeom;
+    int mFeaturesToLabel; // total features that will probably be labeled, may be less (figured before PAL)
+    int mFeatsSendingToPal; // total features tested for sending into PAL (relative to maxNumLabels)
+    int mFeatsRegPal; // number of features registered in PAL, when using limitNumLabels
 
   private:
     /**Checks if a feature is larger than a minimum size (in mm)
diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp
index 886e1f6b8a0..9d7e8c5c226 100644
--- a/src/core/qgsvectorlayer.cpp
+++ b/src/core/qgsvectorlayer.cpp
@@ -5699,6 +5699,21 @@ void QgsVectorLayer::prepareLabelingAndDiagrams( QgsRenderContext& rendererConte
     labeling = true;
   }
 
+  if ( labeling )
+  {
+    // see if feature count limit is set for labeling
+    QgsPalLayerSettings& palyr = rendererContext.labelingEngine()->layer( this->id() );
+    if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
+    {
+      select( QgsAttributeList(), rendererContext.extent() );
+      // total number of features that may be labeled
+      QgsFeature ftr;
+      int nFeatsToLabel = 0;
+      while ( nextFeature( ftr ) ) { nFeatsToLabel += 1; }
+      palyr.mFeaturesToLabel = nFeatsToLabel;
+    }
+  }
+
   //register diagram layers
   if ( mDiagramRenderer && mDiagramLayerSettings )
   {
diff --git a/src/ui/qgslabelingguibase.ui b/src/ui/qgslabelingguibase.ui
index 545936a61a5..bae1aa65b3a 100644
--- a/src/ui/qgslabelingguibase.ui
+++ b/src/ui/qgslabelingguibase.ui
@@ -2780,7 +2780,7 @@
                    </sizepolicy>
                   </property>
                   <property name="text">
-                   <string>Limit number of labels drawn to</string>
+                   <string>Limit number of features to be labeled to</string>
                   </property>
                  </widget>
                 </item>
@@ -2795,6 +2795,9 @@
                     <height>0</height>
                    </size>
                   </property>
+                  <property name="toolTip">
+                   <string>Number of features sent to labeling engine, though not all may be labeled</string>
+                  </property>
                   <property name="alignment">
                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
                   </property>
@@ -2855,7 +2858,7 @@
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>686</width>
+            <width>488</width>
             <height>981</height>
            </rect>
           </property>