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

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sat Jul 25 07:21:45 EDT 2009


Author: wonder
Date: 2009-07-25 07:21:44 -0400 (Sat, 25 Jul 2009)
New Revision: 11176

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/geomfunction.cpp
   branches/symbology-ng-branch/src/core/pal/geomfunction.h
   branches/symbology-ng-branch/src/core/pal/labelposition.cpp
   branches/symbology-ng-branch/src/core/pal/labelposition.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.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:
Multipart labels, advanced label info, added basic support for curved labels (some code taken from Mapnik: placement_finder.cpp)


Modified: branches/symbology-ng-branch/src/core/pal/feature.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/feature.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/feature.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -70,6 +70,7 @@
 
     label_x = -1;
     label_y = -1;
+    labelInfo = NULL;
 
     xmin = feat->minmax[0];
     xmax = feat->minmax[2];
@@ -532,6 +533,242 @@
   }
 
 
+  StraightLabelPosition* Feature::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int orientation, int index, double distance )
+  {
+    // Check that the given distance is on the given index and find the correct index and distance if not
+    while (distance < 0 && index > 1)
+    {
+      index--;
+      distance += path_distances[index];
+    }
+
+    if (index <= 1 && distance < 0) // We've gone off the start, fail out
+    {
+      std::cerr << "err1" << std::endl;
+      return NULL;
+    }
+
+    // Same thing, checking if we go off the end
+    while (index < path_positions->nbPoints && distance > path_distances[index])
+    {
+      distance -= path_distances[index];
+      index += 1;
+    }
+    if (index >= path_positions->nbPoints)
+    {
+      std::cerr << "err2" << std::endl;
+      return NULL;
+    }
+
+    // Keep track of the initial index,distance incase we need to re-call get_placement_offset
+    int initial_index = index;
+    double initial_distance = distance;
+
+    double string_height = labelInfo->label_height;
+    double old_x = path_positions->x[index-1];
+    double old_y = path_positions->y[index-1];
+
+    double new_x = path_positions->x[index];
+    double new_y = path_positions->y[index];
+
+    double dx = new_x - old_x;
+    double dy = new_y - old_y;
+
+    double segment_length = path_distances[index];
+    if (segment_length == 0)
+    {
+      // Not allowed to place across on 0 length segments or discontinuities
+      std::cerr << "err3" << std::endl;
+      return NULL;
+    }
+
+    StraightLabelPosition* slp = NULL;
+    StraightLabelPosition* slp_tmp = NULL;
+    // current_placement = placement_result()
+    double xBase = old_x + dx*distance/segment_length;
+    double yBase = old_y + dy*distance/segment_length;
+    double angle = atan2(-dy, dx);
+
+    bool orientation_forced = (orientation != 0); // Whether the orientation was set by the caller
+    if (!orientation_forced)
+      orientation = (angle > 0.55*M_PI || angle < -0.45*M_PI ? -1 : 1);
+
+    int upside_down_char_count = 0; // Count of characters that are placed upside down.
+
+    for (int i = 0; i < labelInfo->char_num; i++)
+    {
+      double last_character_angle = angle;
+
+      // grab the next character according to the orientation
+      LabelInfo::CharacterInfo& ci = (orientation > 0 ? labelInfo->char_info[i] : labelInfo->char_info[labelInfo->char_num-i-1]);
+
+      // Coordinates this character will start at
+      if (segment_length == 0)
+      {
+        // Not allowed to place across on 0 length segments or discontinuities
+        std::cerr << "err4" << std::endl;
+        return NULL;
+      }
+
+      double start_x = old_x + dx*distance/segment_length;
+      double start_y = old_y + dy*distance/segment_length;
+      // Coordinates this character ends at, calculated below
+      double end_x = 0;
+      double end_y = 0;
+
+      std::cerr << "segment len " << segment_length << "   distance " << distance << std::endl;
+      if (segment_length - distance  >= ci.width)
+      {
+        // if the distance remaining in this segment is enough, we just go further along the segment
+        distance += ci.width;
+        end_x = old_x + dx*distance/segment_length;
+        end_y = old_y + dy*distance/segment_length;
+      }
+      else
+      {
+        // If there isn't enough distance left on this segment
+        // then we need to search until we find the line segment that ends further than ci.width away
+        do
+        {
+          old_x = new_x;
+          old_y = new_y;
+          index++;
+          if (index >= path_positions->nbPoints) // Bail out if we run off the end of the shape
+          {
+            std::cerr << "err5" << std::endl;
+            return NULL;
+          }
+          new_x = path_positions->x[index];
+          new_y = path_positions->y[index];
+          dx = new_x - old_x;
+          dy = new_y - old_y;
+          segment_length = path_distances[index];
+
+          std::cerr << "-> " << sqrt(pow(start_x - new_x,2) + pow(start_y - new_y,2)) << " vs " << ci.width << std::endl;
+
+        } while (sqrt(pow(start_x - new_x,2) + pow(start_y - new_y,2)) < ci.width); // Distance from start_ to new_
+
+        // Calculate the position to place the end of the character on
+        findLineCircleIntersection( start_x, start_y, ci.width, old_x, old_y, new_x, new_y, end_x, end_y);
+
+        // Need to calculate distance on the new segment
+        distance = sqrt(pow(old_x - end_x,2) + pow(old_y - end_y,2));
+      }
+
+      // Calculate angle from the start of the character to the end based on start_/end_ position
+      angle = atan2(start_y-end_y, end_x-start_x);
+      //angle = atan2(end_y-start_y, end_x-start_x);
+
+      // Test last_character_angle vs angle
+      // since our rendering angle has changed then check against our
+      // max allowable angle change.
+      double angle_delta = last_character_angle - angle;
+      // normalise between -180 and 180
+      while (angle_delta > M_PI) angle_delta -= 2*M_PI;
+      while (angle_delta < -M_PI) angle_delta += 2*M_PI;
+      if (labelInfo->max_char_angle_delta > 0 && fabs(angle_delta) > labelInfo->max_char_angle_delta*(M_PI/180))
+      {
+        std::cerr << "err6" << std::endl;
+        return NULL;
+      }
+
+      double render_angle = angle;
+
+      double render_x = start_x;
+      double render_y = start_y;
+
+      // Center the text on the line
+      //render_x -= ((string_height/2.0) - 1.0)*math.cos(render_angle+math.pi/2)
+      //render_y += ((string_height/2.0) - 1.0)*math.sin(render_angle+math.pi/2)
+
+      if (orientation < 0)
+      {
+        // rotate in place
+        render_x += ci.width*cos(render_angle); //- (string_height-2)*sin(render_angle);
+        render_y -= ci.width*sin(render_angle); //+ (string_height-2)*cos(render_angle);
+        render_angle += M_PI;
+      }
+
+      std::cerr << "adding part: " << render_x << "  " << render_y << std::endl;
+      StraightLabelPosition* tmp = new StraightLabelPosition(0, render_x /*- xBase*/, render_y /*- yBase*/, ci.width, string_height, -render_angle, 0.0001, this);
+      tmp->setPartId( orientation > 0 ? i : labelInfo->char_num-i-1 );
+      if (slp == NULL)
+        slp = tmp;
+      else
+        slp_tmp->setNextPart(tmp);
+      slp_tmp = tmp;
+
+      //current_placement.add_node(ci.character,render_x, -render_y, render_angle);
+      //current_placement.add_node(ci.character,render_x - current_placement.starting_x, render_y - current_placement.starting_y, render_angle)
+
+      // Normalise to 0 <= angle < 2PI
+      while (render_angle >= 2*M_PI) render_angle -= 2*M_PI;
+      while (render_angle < 0) render_angle += 2*M_PI;
+
+      if (render_angle > M_PI/2 && render_angle < 1.5*M_PI)
+        upside_down_char_count++;
+    }
+    // END FOR
+
+    // If we placed too many characters upside down
+    if (upside_down_char_count >= labelInfo->char_num/2.0)
+    {
+      // if we auto-detected the orientation then retry with the opposite orientation
+      if (!orientation_forced)
+      {
+        orientation = -orientation;
+        slp = curvedPlacementAtOffset(path_positions, path_distances, orientation, initial_index, initial_distance);
+      }
+      else
+      {
+        // Otherwise we have failed to find a placement
+        std::cerr << "err7" << std::endl;
+        return NULL;
+      }
+    }
+
+    return slp;
+  }
+
+  int Feature::setPositionForLineCurved( LabelPosition ***lPos, PointSet* mapShape )
+  {
+    // label info must be present
+    if (labelInfo == NULL || labelInfo->char_num == 0)
+      return 0;
+
+    // distance calculation
+    double* path_distances = new double[mapShape->nbPoints];
+    double old_x, old_y, new_x, new_y;
+    for (int i = 0; i < mapShape->nbPoints; i++)
+    {
+      if (i == 0)
+        path_distances[i] = 0;
+      else
+        path_distances[i] = sqrt( pow(old_x - mapShape->x[i], 2) + pow(old_y - mapShape->y[i],2) );
+      old_x = mapShape->x[i];
+      old_y = mapShape->y[i];
+    }
+
+    // TODO: generate more labels
+
+    // generate curved label
+    StraightLabelPosition* slp = curvedPlacementAtOffset(mapShape, path_distances, 0, 1, 0.0);
+
+    if (!slp)
+      return 0;
+
+    // TODO: evaluate cost
+
+    int nbp = 1;
+    ( *lPos ) = new LabelPosition*[nbp];
+    (*lPos)[0] = slp;
+
+    return nbp;
+  }
+
+
+
+
   /*
    *             seg 2
    *     pt3 ____________pt2
@@ -843,7 +1080,10 @@
         releaseCoordinates();
         break;
       case GEOS_LINESTRING:
-        nbp = setPositionForLine( scale, lPos, mapShape, delta );
+        if ( layer->getArrangement() == P_CURVED )
+          nbp = setPositionForLineCurved( lPos, mapShape );
+        else
+          nbp = setPositionForLine( scale, lPos, mapShape, delta );
         break;
 
       case GEOS_POLYGON:

Modified: branches/symbology-ng-branch/src/core/pal/feature.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/feature.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/feature.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -48,7 +48,33 @@
 
 namespace pal
 {
+  /** optional additional info about label (for curved labels) */
+  class LabelInfo
+  {
+  public:
+    typedef struct
+    {
+        ushort chr;
+        double width;
+    } CharacterInfo;
 
+    LabelInfo(int num, double height)
+    {
+      max_char_angle_delta = 20;
+      label_height = height;
+      char_num = num;
+      char_info = new CharacterInfo[num];
+    }
+    ~LabelInfo() { delete [] char_info; }
+
+    double max_char_angle_delta;
+    double label_height;
+    int char_num;
+    CharacterInfo* char_info;
+  };
+
+  class StraightLabelPosition;
+
   /**
    * \brief Main class to handle feature
    */
@@ -59,6 +85,7 @@
       //int id;   /* feature no id into layer */
       double label_x;
       double label_y;
+      LabelInfo* labelInfo; // optional
 
       int nbSelfObs;
       PointSet **selfObs;
@@ -105,7 +132,15 @@
        */
       int setPositionForLine( double scale, LabelPosition ***lPos, PointSet *mapShape, double delta_width );
 
+      StraightLabelPosition* curvedPlacementAtOffset( PointSet* path_positions, double* path_distances,
+                                                      int orientation, int index, double distance );
+
       /**
+       * Generate curved candidates for line features
+       */
+      int setPositionForLineCurved( LabelPosition ***lPos, PointSet* mapShape );
+
+      /**
        * \brief generate candidates for point feature
        * Generate candidates for point features
        * \param scale map scale is 1:scale
@@ -116,7 +151,6 @@
       int setPositionForPolygon( double scale, LabelPosition ***lPos, PointSet *mapShape, double delta_width );
 
 
-
       /**
        * \brief Feature against problem bbox
        * \param bbox[0] problem x min
@@ -244,6 +278,9 @@
       int getNumSelfObstacles() const { return nbSelfObs; }
       PointSet* getSelfObstacle(int i) { return selfObs[i]; }
 
+      void setLabelInfo(LabelInfo* info) { labelInfo = info; }
+
+
   };
 
 } // end namespace pal

Modified: branches/symbology-ng-branch/src/core/pal/geomfunction.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/geomfunction.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/geomfunction.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -626,5 +626,39 @@
 #endif
 
 
+    void findLineCircleIntersection(double cx, double cy, double radius,
+                                  double x1, double y1, double x2, double y2,
+                                  double& xRes, double& yRes)
+    {
+      double dx = x2 - x1;
+      double dy = y2 - y1;
 
+      double A = dx * dx + dy * dy;
+      double B = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
+      double C = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - radius * radius;
+
+      double det = B * B - 4 * A * C;
+      if (A <= 0.0000001 || det < 0)
+          // Should never happen, No real solutions.
+          return;
+
+      if (det == 0)
+      {
+          // Could potentially happen.... One solution.
+          double t = -B / (2 * A);
+          xRes = x1 + t * dx;
+          yRes = y1 + t * dy;
+      }
+      else
+      {
+          // Two solutions.
+          // Always use the 1st one
+          // We only really have one solution here, as we know the line segment will start in the circle and end outside
+          double t = (-B + sqrt(det)) / (2 * A);
+          xRes = x1 + t * dx;
+          yRes = y1 + t * dy;
+      }
+    }
+
+
 } // end namespace

Modified: branches/symbology-ng-branch/src/core/pal/geomfunction.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/geomfunction.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/geomfunction.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -81,6 +81,9 @@
 
 
 
+  void findLineCircleIntersection(double cx, double cy, double radius,
+                                  double x1, double y1, double x2, double y2,
+                                  double& xRes, double& yRes);
 
 
   int convexHull( int *id, const double* const x, const double* const y, int n );

Modified: branches/symbology-ng-branch/src/core/pal/labelposition.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/labelposition.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/labelposition.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -61,7 +61,7 @@
   }
 
   StraightLabelPosition::StraightLabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, Feature *feature )
-    : LabelPosition( id, cost, feature ), alpha( alpha ), w( w ), h( h )
+    : LabelPosition( id, cost, feature ), alpha( alpha ), w( w ), h( h ), nextPart(NULL), partId(-1)
   {
 
     // alpha take his value bw 0 and 2*pi rad
@@ -134,7 +134,10 @@
         return true;
     }
 
-    return false;
+    if (nextPart)
+      return nextPart->isIn(bbox);
+    else
+      return false;
 
   }
 
@@ -191,7 +194,18 @@
       }
 
       if ( d1 == -1 || d2 == -1 ) // disjoint
-        return false;
+      {
+        if ( ls->getNextPart() )
+        {
+          if ( isInConflict(ls->getNextPart()) )
+            return true;
+        }
+
+        if (nextPart)
+          return nextPart->isInConflict( lp );
+        else
+          return false;
+      }
     }
     return true;
   }
@@ -237,10 +251,18 @@
 
   void StraightLabelPosition::getBoundingBox(double amin[2], double amax[2]) const
   {
-    amin[0] = DBL_MAX;
-    amax[0] = -DBL_MAX;
-    amin[1] = DBL_MAX;
-    amax[1] = -DBL_MAX;
+    if (nextPart)
+    {
+      //std::cout << "using next part" <<
+      nextPart->getBoundingBox(amin, amax);
+    }
+    else
+    {
+      amin[0] = DBL_MAX;
+      amax[0] = -DBL_MAX;
+      amin[1] = DBL_MAX;
+      amax[1] = -DBL_MAX;
+    }
     for ( int c = 0;c < 4;c++ )
     {
       if ( x[c] < amin[0] )
@@ -341,8 +363,10 @@
   {
     LabelPosition *lp2 = ( LabelPosition* ) ctx;
 
+    //std::cerr << "checking " << lp2->getFeature()->getUID() << " x " << lp->getFeature()->getUID() << std::endl;
     if ( lp2->isInConflict( lp ) )
     {
+      //std::cerr << "conflict!" << std::endl;
       lp2->nbOverlap++;
     }
 
@@ -433,6 +457,9 @@
         dist_min = dist;
     }
 
+    if (nextPart && dist_min > 0)
+      return min( dist_min, nextPart->getDistanceToPoint(xp, yp) );
+
     return dist_min;
   }
 
@@ -460,6 +487,10 @@
         }
       }
     }
+
+    if (nextPart)
+      return nextPart->isBorderCrossingLine( feat );
+
     return false;
   }
 
@@ -490,6 +521,8 @@
     if ( isPointInPolygon( npol, xp, yp, px, py ) )
       count += 4; // virtually 4 points
 
+    // TODO: count with nextFeature
+
     return count;
   }
 

Modified: branches/symbology-ng-branch/src/core/pal/labelposition.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/labelposition.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/labelposition.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -78,6 +78,8 @@
 
       // virtual functions
 
+      virtual ~LabelPosition() {}
+
       /**
        * \brief is the labelposition in the bounding-box ?
        *
@@ -124,7 +126,7 @@
       int getProblemFeatureId() const { return probFeat; }
       /** set problem feature ID and assigned label candidate ID.
        *  called from pal.cpp during extraction */
-      void setProblemIds( int probFid, int lpId ) { probFeat = probFid; id = lpId; }
+      virtual void setProblemIds( int probFid, int lpId ) { probFeat = probFid; id = lpId; }
 
       /** return pointer to layer's name. used for stats */
       char* getLayerName() const;
@@ -212,6 +214,8 @@
                              double alpha, double cost,
                              Feature *feature );
 
+      ~StraightLabelPosition() { delete nextPart; }
+
       // virtual functions
 
       virtual bool isIn( double *bbox );
@@ -252,12 +256,24 @@
 
       void print();
 
+      StraightLabelPosition* getNextPart() const { return nextPart; }
+      void setNextPart(StraightLabelPosition* next) { nextPart = next; }
+
+      // -1 if not multi-part
+      int getPartId() const { return partId; }
+      void setPartId(int id) { partId = id; }
+
+      void setProblemIds( int probFid, int lpId ) {
+        LabelPosition::setProblemIds(probFid, lpId);
+        if (nextPart) nextPart->setProblemIds(probFid, lpId); }
+      
     protected:
       double x[4], y[4];
       double alpha;
       double w;
       double h;
-
+      StraightLabelPosition* nextPart;
+      int partId;
     };
 
 } // end namespac

Modified: branches/symbology-ng-branch/src/core/pal/layer.cpp
===================================================================
--- branches/symbology-ng-branch/src/core/pal/layer.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/layer.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -550,6 +550,39 @@
   return ret;
 }
 
+
+void Layer::setFeatureLabelInfo( const char *geom_id, LabelInfo* labelInfo )
+{
+  if ( !labelInfo )
+  {
+    throw new PalException::ValueNotInRange();
+    return;
+  }
+
+  modMutex->lock();
+  Cell<Feature*>* it = getFeatureIt( geom_id );
+
+  if ( it )
+  {
+    Feature *feat = it->item;
+    int nb = feat->getNumParts();
+
+    for ( int i = 0;i < nb;i++ )
+    {
+      feat = it->item;
+      feat->setLabelInfo(labelInfo);
+      it = it->next;
+    }
+  }
+  else
+  {
+    modMutex->unlock();
+    throw new PalException::UnknownFeature();
+  }
+  modMutex->unlock();
+}
+
+
 void Layer::setLabelUnit( Units label_unit )
 {
   if ( label_unit == PIXEL || label_unit == METER )

Modified: branches/symbology-ng-branch/src/core/pal/layer.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/layer.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/layer.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -53,6 +53,7 @@
   class Feature;
   class Pal;
   class SimpleMutex;
+  class LabelInfo;
 
   class Feat;
 
@@ -343,6 +344,10 @@
        */
       double getFeatureDistlabel( const char *geom_id );
 
+      /**
+       * add more detailed information about layer (character widths and more)
+       */
+      void setFeatureLabelInfo( const char *geom_id, LabelInfo* labelInfo );
   };
 
 } // end namespace pal

Modified: branches/symbology-ng-branch/src/core/pal/pal.h
===================================================================
--- branches/symbology-ng-branch/src/core/pal/pal.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/core/pal/pal.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -100,6 +100,7 @@
     P_POINT = 0, /**< arranges candidates around a point (centroid for polygon)*/
     P_POINT_OVER, /** arranges candidates over a point (centroid for polygon)*/
     P_LINE, /**< Only for lines and polygons, arranges candidates over the line or the polygon perimeter */
+    P_CURVED, /** Only for lines, labels along the line */
     P_HORIZ, /**< Only for polygon, arranges candidates horizontaly */
     P_FREE /**< Only for polygon, arranges candidates with respect of polygon orientation */
   };

Modified: branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/plugins/labeling/labelinggui.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -93,6 +93,9 @@
         else
           radOrientationLine->setChecked(true);
         break;
+      case LayerSettings::Curved:
+        radLineCurved->setChecked(true);
+        break;
       case LayerSettings::Horizontal:
         radPolygonHorizontal->setChecked(true);
         radLineHorizontal->setChecked(true);
@@ -142,7 +145,7 @@
   // setup connection to changes in the placement
   QRadioButton* placementRadios[] = {
     radAroundPoint, radOverPoint, // point
-    radLineParallel, radLineHorizontal, // line
+    radLineParallel, radLineCurved, radLineHorizontal, // line
     radAroundCentroid, radPolygonHorizontal, radPolygonFree, radPolygonPerimeter // polygon
   };
   for (int i = 0; i < sizeof(placementRadios)/sizeof(QRadioButton*); i++)
@@ -197,6 +200,10 @@
     if (radOrientationMap->isChecked())
       lyr.placementFlags |= LayerSettings::MapOrientation;
   }
+  else if ( stackedPlacement->currentWidget() == pageLine && radLineCurved->isChecked() )
+  {
+    lyr.placement = LayerSettings::Curved;
+  }
   else if ( (stackedPlacement->currentWidget() == pageLine && radLineHorizontal->isChecked())
     || (stackedPlacement->currentWidget() == pagePolygon && radPolygonHorizontal->isChecked()) )
   {

Modified: branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/plugins/labeling/labelingguibase.ui	2009-07-25 11:21:44 UTC (rev 11176)
@@ -77,7 +77,7 @@
       <item>
        <widget class="QStackedWidget" name="stackedPlacement">
         <property name="currentIndex">
-         <number>0</number>
+         <number>1</number>
         </property>
         <widget class="QWidget" name="pagePoint">
          <layout class="QVBoxLayout" name="verticalLayout_2">
@@ -113,6 +113,13 @@
            </widget>
           </item>
           <item>
+           <widget class="QRadioButton" name="radLineCurved">
+            <property name="text">
+             <string>curved</string>
+            </property>
+           </widget>
+          </item>
+          <item>
            <widget class="QRadioButton" name="radLineHorizontal">
             <property name="text">
              <string>horizontal</string>

Modified: branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/plugins/labeling/pallabeling.cpp	2009-07-25 11:21:44 UTC (rev 11176)
@@ -33,7 +33,7 @@
 class MyLabel : public PalGeometry
 {
 public:
-  MyLabel(int id, QString text, GEOSGeometry* g): mG(g), mText(text), mId(id)
+  MyLabel(int id, QString text, GEOSGeometry* g): mG(g), mText(text), mId(id), mInfo(NULL)
   {
     mStrId = QByteArray::number(id);
   }
@@ -41,6 +41,7 @@
   ~MyLabel()
   {
     if (mG) GEOSGeom_destroy(mG);
+    delete mInfo;
   }
 
   // getGeosGeometry + releaseGeosGeometry is called twice: once when adding, second time when labeling
@@ -57,11 +58,30 @@
   const char* strId() { return mStrId.data(); }
   QString text() { return mText; }
 
+  pal::LabelInfo* info(QFontMetrics* fm, const QgsMapToPixel* xform)
+  {
+    if (mInfo) return mInfo;
+
+    // create label info!
+    QgsPoint ptZero = xform->toMapCoordinates( 0,0 );
+    QgsPoint ptSize = xform->toMapCoordinates( 0,-fm->height() );
+
+    mInfo = new pal::LabelInfo( mText.count(), ptSize.y()-ptZero.y() );
+    for (int i = 0; i < mText.count(); i++)
+    {
+      mInfo->char_info[i].chr = mText[i].unicode();
+      ptSize = xform->toMapCoordinates( fm->width( mText[i] ), 0 );
+      mInfo->char_info[i].width = ptSize.x()-ptZero.x();
+    }
+    return mInfo;
+  }
+
 protected:
   GEOSGeometry* mG;
   QString mText;
   QByteArray mStrId;
   int mId;
+  LabelInfo* mInfo;
 };
 
 // -------------
@@ -133,6 +153,9 @@
   if (!palLayer->registerFeature(lbl->strId(), lbl, labelX, labelY))
     return;
 
+  // TODO: only for placement which needs character info
+  palLayer->setFeatureLabelInfo( lbl->strId(), lbl->info( fontMetrics, xform ) );
+
   // TODO: allow layer-wide feature dist in PAL...?
   if (dist != 0)
     palLayer->setFeatureDistlabel(lbl->strId(), fabs(ptOne.x()-ptZero.x())* dist);
@@ -243,6 +266,7 @@
     case LayerSettings::AroundPoint: arrangement = P_POINT; break;
     case LayerSettings::OverPoint:   arrangement = P_POINT_OVER; break;
     case LayerSettings::Line:        arrangement = P_LINE; break;
+    case LayerSettings::Curved:      arrangement = P_CURVED; break;
     case LayerSettings::Horizontal:  arrangement = P_HORIZ; break;
     case LayerSettings::Free:        arrangement = P_FREE; break;
   }
@@ -350,19 +374,7 @@
       {
         pal::StraightLabelPosition* lp = (pal::StraightLabelPosition*) problem->getFeatureCandidate(i, j);
 
-        QgsPoint outPt = xform->transform(lp->getX(), lp->getY());
-        QgsPoint outPt2 = xform->transform(lp->getX()+lp->getWidth(), lp->getY()+lp->getHeight());
-
-        painter->save();
-        painter->translate( QPointF(outPt.x(), outPt.y()) );
-        painter->rotate(-lp->getAlpha() * 180 / M_PI );
-        QRectF rect(0,0, outPt2.x()-outPt.x(), outPt2.y()-outPt.y());
-        painter->drawRect(rect);
-        painter->restore();
-
-        // save the rect
-        rect.moveTo(outPt.x(),outPt.y());
-        mCandidates.append( LabelCandidate(rect, lp->getCost() * 1000) );
+        drawLabelCandidateRect(lp, painter, xform);
       }
     }
   }
@@ -379,26 +391,7 @@
   {
     StraightLabelPosition* label = (StraightLabelPosition*) *it;
 
-    QgsPoint outPt = xform->transform(label->getX(), label->getY());
-
-    // TODO: optimize access :)
-    const LayerSettings& lyr = layer(label->getLayerName());
-
-    QString text = ((MyLabel*)label->getFeature()->getUserGeometry())->text();
-
-    // shift by one as we have 2px border
-    painter->save();
-    painter->translate( QPointF(outPt.x()+1, outPt.y()-1-lyr.fontBaseline) );
-    painter->rotate(-label->getAlpha() * 180 / M_PI );
-    painter->setFont( lyr.textFont );
-
-    if (lyr.bufferSize != 0)
-      drawLabelBuffer(painter, text, lyr.bufferSize, lyr.bufferColor);
-
-    painter->setPen( lyr.textColor );
-    painter->drawText(0,0, text);
-    painter->restore();
-
+    drawLabel( label, painter, xform );
   }
 
   std::cout << "LABELING draw:   " << t.elapsed() << "ms" << std::endl;
@@ -443,6 +436,58 @@
   return mSearch;
 }
 
+void PalLabeling::drawLabelCandidateRect( pal::StraightLabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform )
+{
+  QgsPoint outPt = xform->transform(lp->getX(), lp->getY());
+  QgsPoint outPt2 = xform->transform(lp->getX()+lp->getWidth(), lp->getY()+lp->getHeight());
+
+  painter->save();
+  painter->translate( QPointF(outPt.x(), outPt.y()) );
+  painter->rotate(-lp->getAlpha() * 180 / M_PI );
+  QRectF rect(0,0, outPt2.x()-outPt.x(), outPt2.y()-outPt.y());
+  painter->drawRect(rect);
+  painter->restore();
+
+  // save the rect
+  rect.moveTo(outPt.x(),outPt.y());
+  mCandidates.append( LabelCandidate(rect, lp->getCost() * 1000) );
+
+  // show all parts of the multipart label
+  if (lp->getNextPart())
+    drawLabelCandidateRect(lp->getNextPart(), painter, xform);
+}
+
+
+void PalLabeling::drawLabel( pal::StraightLabelPosition* label, QPainter* painter, const QgsMapToPixel* xform)
+{
+  QgsPoint outPt = xform->transform(label->getX(), label->getY());
+
+  // TODO: optimize access :)
+  const LayerSettings& lyr = layer(label->getLayerName());
+
+  QString text = ((MyLabel*)label->getFeature()->getUserGeometry())->text();
+
+  // shift by one as we have 2px border
+  painter->save();
+  painter->translate( QPointF(outPt.x()+1, outPt.y()-1-lyr.fontBaseline) );
+  painter->rotate(-label->getAlpha() * 180 / M_PI );
+  painter->setFont( lyr.textFont );
+
+  if (lyr.bufferSize != 0)
+    drawLabelBuffer(painter, text, lyr.bufferSize, lyr.bufferColor);
+
+  painter->setPen( lyr.textColor );
+  if (label->getPartId() == -1)
+    painter->drawText(0,0, text);
+  else
+    painter->drawText(0,0, QString( text[label->getPartId()] ) );
+  painter->restore();
+
+  if (label->getNextPart())
+    drawLabel( label->getNextPart(), painter, xform );
+}
+
+
 void PalLabeling::drawLabelBuffer(QPainter* p, QString text, int size, QColor color)
 {
   p->save();

Modified: branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h
===================================================================
--- branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h	2009-07-24 19:31:31 UTC (rev 11175)
+++ branches/symbology-ng-branch/src/plugins/labeling/pallabeling.h	2009-07-25 11:21:44 UTC (rev 11176)
@@ -16,6 +16,7 @@
 {
   class Pal;
   class Layer;
+  class StraightLabelPosition;
 }
 
 class QgsMapToPixel;
@@ -36,6 +37,7 @@
     AroundPoint, // Point / Polygon
     OverPoint, // Point / Polygon
     Line, // Line / Polygon
+    Curved, // Line
     Horizontal, // Polygon
     Free // Polygon
   };
@@ -120,6 +122,8 @@
     static void registerFeatureHook(QgsFeature& f, void* layerContext);
 
 
+    void drawLabelCandidateRect( pal::StraightLabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform );
+    void drawLabel( pal::StraightLabelPosition* label, QPainter* painter, const QgsMapToPixel* xform);
     static void drawLabelBuffer(QPainter* p, QString text, int size, QColor color);
 
 protected:



More information about the QGIS-commit mailing list