[QGIS Commit] r11293 - in branches/symbology-ng-branch/src: core/pal plugins/labeling

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sat Aug 8 19:41:34 EDT 2009


Author: wonder
Date: 2009-08-08 19:41:33 -0400 (Sat, 08 Aug 2009)
New Revision: 11293

Modified:
   branches/symbology-ng-branch/src/core/pal/feature.cpp
   branches/symbology-ng-branch/src/core/pal/feature.h
   branches/symbology-ng-branch/src/core/pal/layer.cpp
   branches/symbology-ng-branch/src/core/pal/layer.h
   branches/symbology-ng-branch/src/core/pal/pal.cpp
   branches/symbology-ng-branch/src/core/pal/pointset.cpp
   branches/symbology-ng-branch/src/core/pal/pointset.h
   branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp
   branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui
   branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp
   branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h
Log:
Added possibility to merge adjacent lines with the same label text so they're labeled only once.


Modified: branches/symbology-ng-branch/src/core/pal/feature.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/feature.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/feature.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -77,6 +77,10 @@
   FeaturePart::FeaturePart( Feature *feat, const GEOSGeometry* geom )
    : f(feat), nbHoles(0), holes(NULL)
   {
+    // we'll remove const, but we won't modify that geometry
+    the_geom = const_cast<GEOSGeometry*>(geom);
+    ownsGeom = false; // geometry is owned by Feature class
+
     extractCoords(geom);
 
     holeOf = NULL;
@@ -98,6 +102,12 @@
       delete [] holes;
       holes = NULL;
     }
+
+    if (ownsGeom)
+    {
+      GEOSGeom_destroy(the_geom);
+      the_geom = NULL;
+    }
   }
 
 
@@ -581,7 +591,7 @@
 #endif
       if ( f->layer->arrangement == P_LINE )
       {
-        std::cout << alpha*180/M_PI << std::endl;
+        //std::cout << alpha*180/M_PI << std::endl;
         if ( flags & FLAG_MAP_ORIENTATION )
           reversed = ( alpha >= M_PI/2 || alpha < -M_PI/2 );
 
@@ -1260,5 +1270,36 @@
   }
 
 
+  bool FeaturePart::isConnected(FeaturePart* p2)
+  {
+    return (GEOSTouches(the_geom, p2->the_geom) == 1);
+  }
 
+  bool FeaturePart::mergeWithFeaturePart(FeaturePart* other)
+  {
+    GEOSGeometry* g1 = GEOSGeom_clone(the_geom);
+    GEOSGeometry* g2 = GEOSGeom_clone(other->the_geom);
+    GEOSGeometry* geoms[2] = { g1, g2 };
+    GEOSGeometry* g = GEOSGeom_createCollection(GEOS_MULTILINESTRING, geoms, 2);
+    GEOSGeometry* gTmp = GEOSLineMerge(g);
+    GEOSGeom_destroy(g);
+
+    if (GEOSGeomTypeId(gTmp) != GEOS_LINESTRING)
+    {
+      // sometimes it's not possible to merge lines (e.g. they don't touch at endpoints)
+      GEOSGeom_destroy(gTmp);
+      return false;
+    }
+
+    if (ownsGeom) // delete old geometry if we own it
+      GEOSGeom_destroy(the_geom);
+    // set up new geometry
+    the_geom = gTmp;
+    ownsGeom = true;
+
+    deleteCoords();
+    extractCoords(the_geom);
+    return true;
+  }
+
 } // end namespace pal

Modified: branches/symbology-ng-branch/src/core/pal/feature.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/feature.h	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/feature.h	2009-08-08 23:41:33 UTC (rev 11293)
@@ -117,6 +117,7 @@
       PointSet **holes;
 
       GEOSGeometry *the_geom;
+      bool ownsGeom;
 
       /** \brief read coordinates from a GEOS geom */
       void extractCoords( const GEOSGeometry* geom );
@@ -259,6 +260,12 @@
       int getNumSelfObstacles() const { return nbHoles; }
       PointSet* getSelfObstacle(int i) { return holes[i]; }
 
+      /** check whether this part is connected with some other part */
+      bool isConnected(FeaturePart* p2);
+
+      /** merge other (connected) part with this one and save the result in this part (other is unchanged).
+       * Return true on success, false if the feature wasn't modified */
+      bool mergeWithFeaturePart(FeaturePart* other);
   };
 
 } // end namespace pal

Modified: branches/symbology-ng-branch/src/core/pal/layer.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/layer.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/layer.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -62,7 +62,7 @@
       :  pal( pal ), obstacle( obstacle ), active( active ),
          toLabel( toLabel ), label_unit( label_unit ),
          min_scale( min_scale ), max_scale( max_scale ),
-         arrangement( arrangement ), arrangementFlags( 0 ), mode(LabelPerFeature)
+         arrangement( arrangement ), arrangementFlags( 0 ), mode(LabelPerFeature), mergeLines(false)
   {
 
     this->name = new char[strlen( lyrName ) +1];
@@ -73,6 +73,9 @@
     rtree = new RTree<FeaturePart*, double, 2, double>();
     hashtable = new HashTable<Feature*> ( 5281 );
 
+    connectedHashtable = new HashTable< LinkedList<FeaturePart*>* > ( 5391 );
+    connectedTexts = new LinkedList< char* >( strCompare );
+
     if ( defaultPriority < 0.0001 )
       this->defaultPriority = 0.0001;
     else if ( defaultPriority > 1.0 )
@@ -95,8 +98,13 @@
         delete featureParts->pop_front();
       }
       delete featureParts;
+
     }
 
+    // this hashtable and list should be empty if they still exist
+    delete connectedHashtable;
+    double connectedTexts;
+
     // features in the hashtable
     if ( features )
     {
@@ -219,7 +227,7 @@
 
 
 
-bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y )
+bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText )
 {
   if ( !geom_id || label_x < 0 || label_y < 0 )
     return false;
@@ -297,7 +305,7 @@
     }
 
     // feature part is ready!
-    addFeaturePart(fpart);
+    addFeaturePart(fpart, labelText);
 
     first_feat = false;
   }
@@ -310,7 +318,7 @@
   // if using only biggest parts...
   if (mode == LabelPerFeature && biggest_part != NULL)
   {
-    addFeaturePart(biggest_part);
+    addFeaturePart(biggest_part, labelText);
     first_feat = false;
   }
 
@@ -328,7 +336,7 @@
   return !first_feat; // true if we've added something
 }
 
-void Layer::addFeaturePart( FeaturePart* fpart )
+void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
 {
   double bmin[2];
   double bmax[2];
@@ -339,6 +347,28 @@
 
   // add to r-tree for fast spatial access
   rtree->Insert( bmin, bmax, fpart );
+
+  // add to hashtable with equally named feature parts
+  if (mergeLines && labelText)
+  {
+    LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find(labelText);
+    LinkedList< FeaturePart*>* lst;
+    if (lstPtr == NULL)
+    {
+      // entry doesn't exist yet
+      lst = new LinkedList<FeaturePart*>( ptrFeaturePartCompare );
+      connectedHashtable->insertItem(labelText, lst);
+
+      char* txt = new char[strlen(labelText) +1];
+      strcpy(txt, labelText);
+      connectedTexts->push_back(txt);
+    }
+    else
+    {
+      lst = *lstPtr;
+    }
+    lst->push_back(fpart); // add to the list
+  }
 }
 
 
@@ -354,6 +384,78 @@
 }
 
 
+static FeaturePart* _findConnectedPart(FeaturePart* partCheck, LinkedList<FeaturePart*>* otherParts)
+{
+  // iterate in the rest of the parts with the same label
+  Cell<FeaturePart*>* p = otherParts->getFirst();
+  while (p)
+  {
+    if (partCheck->isConnected(p->item))
+    {
+      // stop checking for other connected parts
+      return p->item;
+    }
+    p = p->next;
+  }
 
+  return NULL; // no connected part found...
+}
+
+void Layer::joinConnectedFeatures()
+{  
+  // go through all label texts
+  char* labelText;
+  while ( labelText = connectedTexts->pop_front() )
+  {
+    //std::cerr << "JOIN: " << labelText << std::endl;
+    LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find(labelText);
+    if (!partsPtr)
+      continue; // shouldn't happen
+    LinkedList<FeaturePart*>* parts = *partsPtr;
+
+    // go one-by-one part, try to merge
+    while (parts->size())
+    {
+      // part we'll be checking against other in this round
+      FeaturePart* partCheck = parts->pop_front();
+
+      FeaturePart* otherPart = _findConnectedPart(partCheck, parts);
+      if (otherPart)
+      {
+        //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
+
+        // remove partCheck from r-tree
+        double bmin[2], bmax[2];
+        partCheck->getBoundingBox(bmin, bmax);
+        rtree->Remove(bmin,bmax, partCheck);
+
+        otherPart->getBoundingBox(bmin, bmax);
+
+        // merge points from partCheck to p->item
+        if (otherPart->mergeWithFeaturePart(partCheck))
+        {
+          // reinsert p->item to r-tree (probably not needed)
+          rtree->Remove(bmin,bmax, otherPart);
+          otherPart->getBoundingBox(bmin, bmax);
+          rtree->Insert(bmin, bmax, otherPart);
+        }
+      }
+    }
+
+    // we're done processing feature parts with this particular label text
+    delete parts;
+    *partsPtr = NULL;
+    delete labelText;
+  }
+
+  // we're done processing connected fetures
+  delete connectedHashtable;
+  connectedHashtable = NULL;
+  delete connectedTexts;
+  connectedTexts = NULL;
+}
+
+
+
 } // end namespace
 

Modified: branches/symbology-ng-branch/src/core/pal/layer.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/layer.h	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/layer.h	2009-08-08 23:41:33 UTC (rev 11293)
@@ -101,6 +101,7 @@
       Arrangement arrangement;
 
       LabelMode mode;
+      bool mergeLines;
 
       /** optional flags used for some placement methods */
       unsigned long arrangementFlags;
@@ -109,6 +110,9 @@
       RTree<FeaturePart*, double, 2, double, 8, 4> *rtree;
       HashTable<Feature*> *hashtable;
 
+      HashTable< LinkedList<FeaturePart*>* > * connectedHashtable;
+      LinkedList< char* >* connectedTexts;
+
       SimpleMutex *modMutex;
 
       /**
@@ -140,7 +144,7 @@
       bool isScaleValid( double scale );
 
       /** add newly creted feature part into r tree and to the list */
-      void addFeaturePart( FeaturePart* fpart );
+      void addFeaturePart( FeaturePart* fpart, const char* labelText = NULL );
 
     public:
       /**
@@ -271,6 +275,9 @@
       void setLabelMode( LabelMode m ) { mode = m; }
       LabelMode getLabelMode() const { return mode; }
 
+      void setMergeConnectedLines(bool m) { mergeLines = m; }
+      bool getMergeConnectedLines() const { return mergeLines; }
+
       /**
        * \brief register a feature in the layer
        *
@@ -283,11 +290,14 @@
        *
        * @return true on success (i.e. valid geometry)
        */
-      bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1 );
+      bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1, const char* labelText = NULL );
 
       /** return pointer to feature or NULL if doesn't exist */
       Feature* getFeature( const char* geom_id );
 
+      /** join connected features with the same label text */
+      void joinConnectedFeatures();
+
   };
 
 } // end namespace pal

Modified: branches/symbology-ng-branch/src/core/pal/pal.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/pal.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/pal.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -500,6 +500,10 @@
           // check if this selected layers has been selected by user
           if ( strcmp( layersName[i], layer->name ) == 0 )
           {
+            // check for connected features with the same label text and join them
+            if (layer->getMergeConnectedLines())
+              layer->joinConnectedFeatures();
+
             context->layer = layer;
             context->priority = layersFactor[i];
             // lookup for feature (and generates candidates list)

Modified: branches/symbology-ng-branch/src/core/pal/pointset.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/pointset.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/pointset.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -136,10 +136,7 @@
 
   PointSet::~PointSet()
   {
-    if ( x )
-      delete[] x;
-    if ( y )
-      delete[] y;
+    deleteCoords();
 
     if ( status )
       delete[] status;
@@ -147,7 +144,17 @@
       delete[] cHull;
   }
 
+  void PointSet::deleteCoords()
+  {
+    if ( x )
+      delete[] x;
+    if ( y )
+      delete[] y;
+    x = NULL;
+    y = NULL;
+  }
 
+
   int PointSet::getPath( int start, int stop, int *path_val )
   {
     int nbPt = 0;

Modified: branches/symbology-ng-branch/src/core/pal/pointset.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/pointset.h	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/core/pal/pointset.h	2009-08-08 23:41:33 UTC (rev 11293)
@@ -115,6 +115,8 @@
 
       PointSet( PointSet &ps );
 
+      void deleteCoords();
+
       double xmin;
       double xmax;
       double ymin;

Modified: branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -60,6 +60,8 @@
       Q_ASSERT(0 && "NOOOO!");
   }
 
+  chkMergeLines->setEnabled(layer->geometryType() == QGis::Line);
+
   populateFieldNames();
 
   // load labeling settings from layer
@@ -111,6 +113,7 @@
   sliderPriority->setValue( lyr.priority );
   chkNoObstacle->setChecked( !lyr.obstacle );
   chkLabelPerFeaturePart->setChecked( lyr.labelPerPart );
+  chkMergeLines->setChecked( lyr.mergeLines );
 
   bool scaleBased = (lyr.scaleMin != 0 && lyr.scaleMax != 0);
   chkScaleBasedVisibility->setChecked(scaleBased);
@@ -207,6 +210,7 @@
   lyr.priority = sliderPriority->value();
   lyr.obstacle = !chkNoObstacle->isChecked();
   lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
+  lyr.mergeLines = chkMergeLines->isChecked();
   if (chkScaleBasedVisibility->isChecked())
   {
     lyr.scaleMin = spinScaleMin->value();

Modified: branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui	2009-08-08 23:41:33 UTC (rev 11293)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>454</width>
-    <height>526</height>
+    <width>480</width>
+    <height>610</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -656,6 +656,13 @@
     </widget>
    </item>
    <item>
+    <widget class="QCheckBox" name="chkMergeLines">
+     <property name="text">
+      <string>merge connected lines to avoid duplicate labels</string>
+     </property>
+    </widget>
+   </item>
+   <item>
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <widget class="QCheckBox" name="chkNoObstacle">
@@ -663,7 +670,7 @@
         <bool>true</bool>
        </property>
        <property name="text">
-        <string>not an obstacle</string>
+        <string>features don't act as obstacles for labels</string>
        </property>
       </widget>
      </item>

Modified: branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp	2009-08-08 23:41:33 UTC (rev 11293)
@@ -102,6 +102,7 @@
   bufferSize = 1;
   bufferColor = Qt::white;
   labelPerPart = false;
+  mergeLines = false;
 }
 
 LayerSettings::LayerSettings(const LayerSettings& s)
@@ -121,6 +122,7 @@
   bufferSize = s.bufferSize;
   bufferColor = s.bufferColor;
   labelPerPart = s.labelPerPart;
+  mergeLines = s.mergeLines;
 
   fontMetrics = NULL;
   ct = NULL;
@@ -172,7 +174,8 @@
   scaleMax = layer->customProperty("labeling/scaleMax").toInt();
   bufferSize = layer->customProperty("labeling/bufferSize").toInt();
   bufferColor = _readColor(layer, "labeling/bufferColor");
-  labelPerPart = layer->customProperty("labeling/labelPerPart").toInt();
+  labelPerPart = layer->customProperty("labeling/labelPerPart").toBool();
+  mergeLines = layer->customProperty("labeling/mergeLines").toBool();
 }
 
 void LayerSettings::writeToLayer(QgsVectorLayer* layer)
@@ -199,6 +202,7 @@
   layer->setCustomProperty("labeling/bufferSize", bufferSize);
   _writeColor(layer, "labeling/bufferColor", bufferColor);
   layer->setCustomProperty("labeling/labelPerPart", labelPerPart);
+  layer->setCustomProperty("labeling/mergeLines", mergeLines);
 }
 
 void LayerSettings::calculateLabelSize(QString text, double& labelX, double& labelY)
@@ -228,7 +232,7 @@
   geometries.append(lbl);
 
   // register feature to the layer
-  if (!palLayer->registerFeature(lbl->strId(), lbl, labelX, labelY))
+  if (!palLayer->registerFeature(lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData()))
     return;
 
   // TODO: only for placement which needs character info
@@ -326,6 +330,9 @@
   // set label mode (label per feature is the default)
   l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
 
+  // set whether adjacent lines should be merged
+  l->setMergeConnectedLines( lyr.mergeLines );
+
   // save the pal layer to our layer context (with some additional info)
   lyr.palLayer = l;
   lyr.fieldIndex = fldIndex;

Modified: branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h	2009-08-08 18:45:24 UTC (rev 11292)
+++ branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h	2009-08-08 23:41:33 UTC (rev 11293)
@@ -65,6 +65,7 @@
   int bufferSize;
   QColor bufferColor;
   bool labelPerPart; // whether to label every feature's part or only the biggest one
+  bool mergeLines;
 
   // called from register feature hook
   void calculateLabelSize(QString text, double& labelX, double& labelY);



More information about the QGIS-commit mailing list