Skip to content

Commit f8353d0

Browse files
author
g_j_m
committedApr 29, 2006
Fix for ticket #88.
Implemented code to determine label positions for multipoints, multilinestrings, and multipolygons. Some ratioanlisation of the rendering code too. git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@5394 c8812cc2-4d05-0410-92ff-de0c093fc19c

File tree

2 files changed

+192
-154
lines changed

2 files changed

+192
-154
lines changed
 

‎src/gui/qgslabel.cpp

Lines changed: 174 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ QgsLabel::~QgsLabel()
6666
QString QgsLabel::fieldValue ( int attr, QgsFeature *feature )
6767
{
6868
if ( mLabelField[attr].isEmpty() )
69+
{
6970
return QString();
71+
}
7072

7173
std::vector<QgsFeatureAttribute> fields = feature->attributeMap();
7274

@@ -94,15 +96,14 @@ void QgsLabel::renderLabel( QPainter * painter, QgsRect *viewExtent,
9496
QFont font;
9597
QString value;
9698
QString text;
97-
double scale, x1, x2;
9899

99100
/* Calc scale (not nice) */
100101
QgsPoint point;
101102
point = transform->transform ( 0, 0 );
102-
x1 = point.x();
103+
double x1 = point.x();
103104
point = transform->transform ( 1000, 0 );
104-
x2 = point.x();
105-
scale = (x2-x1)/1000;
105+
double x2 = point.x();
106+
double scale = (x2-x1)*0.001;
106107

107108
/* Text */
108109
value = fieldValue ( Text, feature );
@@ -184,84 +185,22 @@ void QgsLabel::renderLabel( QPainter * painter, QgsRect *viewExtent,
184185
font.setUnderline ( (bool) value.toInt() );
185186
}
186187

187-
188-
/* Coordinates */
189-
double x, y, xoffset, yoffset, ang;
190-
191-
point = labelPoint ( feature );
192-
188+
//
189+
QgsPoint overridePoint;
190+
bool useOverridePoint = false;
193191
value = fieldValue ( XCoordinate, feature );
194192
if ( !value.isEmpty() )
195193
{
196-
point.setX ( value.toDouble() );
194+
overridePoint.setX ( value.toDouble() );
195+
useOverridePoint = true;
197196
}
198197
value = fieldValue ( YCoordinate, feature );
199198
if ( !value.isEmpty() )
200199
{
201-
point.setY ( value.toDouble() );
202-
}
203-
204-
// Convert point to projected units
205-
if (doCoordTransform)
206-
{
207-
try
208-
{
209-
point = (const_cast<QgsCoordinateTransform&>(coordTransform)).transform(point);
210-
}
211-
catch(QgsCsException &cse)
212-
{
213-
#ifdef QGISDEBUG
214-
std::cout << "Caught transform error in QgsLabel::renderLabel(). "
215-
<< "Skipping rendering this label" << std::endl;
216-
#endif
217-
return;
218-
}
219-
}
220-
// and then to canvas units
221-
transform->transform(&point);
222-
x = point.x();
223-
y = point.y();
224-
225-
value = fieldValue ( XOffset, feature );
226-
if ( value.isEmpty() )
227-
{
228-
xoffset = mLabelAttributes->xOffset();
229-
}
230-
else
231-
{
232-
xoffset = value.toDouble();
233-
}
234-
value = fieldValue ( YOffset, feature );
235-
if ( value.isEmpty() )
236-
{
237-
yoffset = mLabelAttributes->yOffset();
238-
}
239-
else
240-
{
241-
yoffset = value.toDouble();
200+
overridePoint.setY ( value.toDouble() );
201+
useOverridePoint = true;
242202
}
243203

244-
// recalc offset to points
245-
if ( mLabelAttributes->offsetType() == QgsLabelAttributes::MapUnits )
246-
{
247-
xoffset *= scale;
248-
yoffset *= scale;
249-
}
250-
251-
value = fieldValue ( Angle, feature );
252-
if ( value.isEmpty() )
253-
{
254-
ang = mLabelAttributes->angle();
255-
}
256-
else
257-
{
258-
ang = value.toDouble();
259-
}
260-
double rad = ang * M_PI/180;
261-
262-
x = x + xoffset * cos(rad) - yoffset * sin(rad);
263-
y = y - xoffset * sin(rad) - yoffset * cos(rad);
264-
265204
/* Alignment */
266205
int alignment;
267206
QFontMetrics fm ( font );
@@ -322,6 +261,105 @@ void QgsLabel::renderLabel( QPainter * painter, QgsRect *viewExtent,
322261
dy = height;
323262
}
324263

264+
// Offset
265+
double xoffset, yoffset;
266+
value = fieldValue ( XOffset, feature );
267+
if ( value.isEmpty() )
268+
{
269+
xoffset = mLabelAttributes->xOffset();
270+
}
271+
else
272+
{
273+
xoffset = value.toDouble();
274+
}
275+
value = fieldValue ( YOffset, feature );
276+
if ( value.isEmpty() )
277+
{
278+
yoffset = mLabelAttributes->yOffset();
279+
}
280+
else
281+
{
282+
yoffset = value.toDouble();
283+
}
284+
285+
// recalc offset to points
286+
if ( mLabelAttributes->offsetType() == QgsLabelAttributes::MapUnits )
287+
{
288+
xoffset *= scale;
289+
yoffset *= scale;
290+
}
291+
292+
// Angle
293+
double ang;
294+
value = fieldValue ( Angle, feature );
295+
if ( value.isEmpty() )
296+
{
297+
ang = mLabelAttributes->angle();
298+
}
299+
else
300+
{
301+
ang = value.toDouble();
302+
}
303+
304+
305+
// Work out a suitable position to put the label for the
306+
// feature. For multi-geometries, put the same label on each
307+
// part.
308+
if (useOverridePoint)
309+
{
310+
renderLabel(painter, overridePoint, doCoordTransform, coordTransform,
311+
transform, text, font, pen, dx, dy,
312+
xoffset, yoffset, ang);
313+
}
314+
else
315+
{
316+
std::vector<QgsPoint> points;
317+
labelPoint ( points, feature );
318+
for (int i = 0; i < points.size(); ++i)
319+
{
320+
renderLabel(painter, points[i], doCoordTransform, coordTransform,
321+
transform, text, font, pen, dx, dy,
322+
xoffset, yoffset, ang);
323+
}
324+
}
325+
}
326+
327+
void QgsLabel::renderLabel(QPainter* painter, QgsPoint point,
328+
bool doCoordTransform,
329+
const QgsCoordinateTransform& coordTransform,
330+
QgsMapToPixel* transform,
331+
QString text, QFont font, QPen pen,
332+
int dx, int dy,
333+
double xoffset, double yoffset,
334+
double ang)
335+
{
336+
// Convert point to projected units
337+
if (doCoordTransform)
338+
{
339+
try
340+
{
341+
point = (const_cast<QgsCoordinateTransform&>(coordTransform)).transform(point);
342+
}
343+
catch(QgsCsException &cse)
344+
{
345+
#ifdef QGISDEBUG
346+
std::cout << "Caught transform error in QgsLabel::renderLabel(). "
347+
<< "Skipping rendering this label" << std::endl;
348+
#endif
349+
return;
350+
}
351+
}
352+
353+
// and then to canvas units
354+
transform->transform(&point);
355+
double x = point.x();
356+
double y = point.y();
357+
358+
static const double rad = ang * M_PI/180;
359+
360+
x = x + xoffset * cos(rad) - yoffset * sin(rad);
361+
y = y - xoffset * sin(rad) - yoffset * cos(rad);
362+
325363
painter->save();
326364
painter->setFont ( font );
327365
painter->translate ( x, y );
@@ -348,6 +386,7 @@ void QgsLabel::renderLabel( QPainter * painter, QgsRect *viewExtent,
348386
}
349387
}
350388
}
389+
351390
painter->setPen ( pen );
352391
painter->drawText ( dx, dy, text );
353392
painter->restore();
@@ -415,32 +454,69 @@ QgsLabelAttributes *QgsLabel::layerAttributes ( void )
415454
return mLabelAttributes;
416455
}
417456

418-
QgsPoint QgsLabel::labelPoint ( QgsFeature *feature )
457+
void QgsLabel::labelPoint ( std::vector<QgsPoint>& points, QgsFeature *feature )
419458
{
459+
unsigned char *geom = feature->getGeometry();
460+
int wkbType;
461+
memcpy(&wkbType, (geom+1), sizeof(wkbType));
462+
463+
QgsPoint point;
464+
465+
switch (wkbType)
466+
{
467+
case QGis::WKBPoint:
468+
case QGis::WKBLineString:
469+
case QGis::WKBPolygon:
470+
labelPoint(point, geom);
471+
points.push_back(point);
472+
break;
473+
case QGis::WKBMultiPoint:
474+
case QGis::WKBMultiLineString:
475+
case QGis::WKBMultiPolygon:
476+
// Return a position for each individual in the multi-feature
477+
int numFeatures = (int)(*(geom + 5));
478+
geom += 9; // now points to start of array of WKB's
479+
for (int i = 0; i < numFeatures; ++i)
480+
{
481+
geom = labelPoint(point, geom);
482+
points.push_back(point);
483+
}
484+
break;
485+
default:
486+
std::cerr << "Unknown geometry type of " << wkbType << '\n';
487+
}
488+
}
489+
490+
unsigned char* QgsLabel::labelPoint ( QgsPoint& point, unsigned char* geom)
491+
{
492+
// Number of bytes that ints and doubles take in the WKB format.
493+
static const unsigned int sizeOfInt = 4;
494+
static const unsigned int sizeOfDouble = 8;
495+
420496
int wkbType;
421497
double *x, *y;
422498
unsigned char *ptr;
423499
int *nPoints;
424-
425-
unsigned char *geom = feature->getGeometry();
500+
// Upon return from this function, this variable will contain a
501+
// pointer to the first byte beyond the current feature.
502+
unsigned char *nextFeature = geom;
426503

427504
memcpy(&wkbType, (geom+1), sizeof(wkbType));
428505

429-
QgsPoint point;
430-
431506
switch (wkbType)
432507
{
433508
case QGis::WKBPoint:
434-
x = (double *) (geom + 5);
435-
y = (double *) (geom + 5 + sizeof(double));
436-
point.setX(*x);
437-
point.setY(*y);
438-
break;
509+
x = (double *) (geom + 5);
510+
y = (double *) (geom + 5 + sizeof(double));
511+
point.set(*x, *y);
512+
nextFeature += 1 + sizeOfInt + sizeOfDouble*2;
513+
break;
439514

440515
case QGis::WKBLineString: // Line center
441516
double dx, dy, tl, l;
442517
ptr = geom + 5;
443518
nPoints = (int *)ptr;
519+
nextFeature += 1 + sizeOfInt*2 + (*nPoints)*sizeOfDouble*2;
444520
ptr = geom + 1 + 2 * sizeof(int);
445521

446522
tl = 0;
@@ -479,7 +555,6 @@ QgsPoint QgsLabel::labelPoint ( QgsFeature *feature )
479555
ptr = geom + 1 + 2 * sizeof(int); // set pointer to the first ring
480556
nPoints = (int *) ptr;
481557
ptr += 4;
482-
483558
sx = sy = 0;
484559
for (int i = 0; i < *nPoints-1; i++)
485560
{
@@ -488,77 +563,25 @@ QgsPoint QgsLabel::labelPoint ( QgsFeature *feature )
488563
}
489564
point.setX ( sx/(*nPoints-1) );
490565
point.setY ( sy/(*nPoints-1) );
491-
492-
break;
493-
case QGis::WKBMultiPolygon:
494-
break; ///// <--------------remove this when code below needs testing..........
566+
// Work out a pointer to the next feature after this one.
567+
int numRings = (int)(*(geom+1+sizeOfInt));
568+
unsigned char* nextRing = nextFeature + 1 + 2*sizeOfInt;
569+
for (int i = 0; i < numRings; ++i)
495570
{
496-
double sx, sy;
497-
unsigned char *ptr;
498-
int idx, kdx;
499-
int *numPolygons, *numRings;
500-
501-
// get the number of polygons
502-
ptr = geom + 5;
503-
numPolygons = (int *) ptr;
504-
#ifdef QGISDEBUG
505-
506-
std::cout << "Finding label point for mutipolygon with "
507-
<< *numPolygons << " parts " << std::endl;
508-
#endif
509-
//for (kdx = 0; kdx < *numPolygons; kdx++)
510-
for (kdx = 0; kdx < 1; kdx++) //just label the first sub polygon
511-
{
512-
//skip the endian and feature type info and
513-
// get number of rings in the polygon
514-
ptr+=14;
515-
numRings = (int *) ptr;
516-
ptr += 4;
517-
#ifdef QGISDEBUG
518-
519-
std::cout << "Multipolygon part " << kdx << " ring iteration " << std::endl;
520-
#endif
521-
522-
for (idx = 0; idx < *numRings; idx++)
523-
{
524-
#ifdef QGISDEBUG
525-
std::cout << "Multipolygon part " << kdx << " ring " << idx
526-
<< std::endl;
527-
#endif
528-
// get number of points in the ring
529-
nPoints = (int *) ptr;
530-
ptr += 4;
531-
sx = sy = 0;
532-
533-
//loop through vertices skipping last which == first
534-
for (int i = 0; i < *nPoints-1; i++)
535-
{
536-
x = (double *)ptr;
537-
ptr += sizeof(double);
538-
y = (double *)ptr;
539-
ptr += sizeof(double);
540-
sx += *x;
541-
sy += *y;
542-
#ifdef QGISDEBUG
543-
544-
std::cout << "\tVertex " << i << " x: " << *x << " y: " << *y << std::endl;
545-
#endif
546-
547-
}
548-
}
549-
#ifdef QGISDEBUG
550-
std::cout << "Setting multipart polygon label point to" << sx/(*nPoints-1) << ", "<< sy/(*nPoints-1) << std::endl;
551-
#endif
552-
553-
point.setX ( sx/(*nPoints-1) );
554-
point.setY ( sy/(*nPoints-1) );
555-
}
556-
break;
571+
int numPoints = (int)(*nextRing);
572+
// get the start of the next ring
573+
nextRing += sizeOfInt + numPoints*sizeOfDouble*2;
574+
}
575+
nextFeature = nextRing;
557576

577+
break;
558578

559-
}
579+
default:
580+
// To get here is a bug because our caller should be filtering
581+
// on wkb type.
582+
break;
560583
}
561-
return QgsPoint ( point );
584+
return nextFeature;
562585
}
563586

564587
void QgsLabel::readXML( const QDomNode& node )

‎src/gui/qgslabel.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class QgsLabel
7777
const QgsCoordinateTransform& coordTransform,
7878
bool doCoordTransform, QgsMapToPixel *transform,
7979
QgsFeature *feature, bool selected, QgsLabelAttributes *classAttributes=0, double sizeScale = 1.);
80-
80+
8181
/** Reads the renderer configuration from an XML file
8282
@param rnode the DOM node to read
8383
*/
@@ -109,9 +109,24 @@ class QgsLabel
109109
QString fieldValue ( int attr, QgsFeature *feature );
110110

111111
private:
112+
/** Does the actual rendering of a label at the given point
113+
*
114+
*/
115+
void renderLabel(QPainter* painter, QgsPoint point,
116+
bool doCoordTransform,
117+
const QgsCoordinateTransform& coordTransform,
118+
QgsMapToPixel* transform,
119+
QString text, QFont font, QPen pen,
120+
int dx, int dy,
121+
double xoffset, double yoffset,
122+
double ang);
123+
112124
/** Get label point for simple feature in map units */
113-
QgsPoint labelPoint ( QgsFeature *feature );
114-
125+
void labelPoint ( std::vector<QgsPoint>&, QgsFeature *feature );
126+
127+
/** Get label point for the given feature in wkb format. */
128+
unsigned char* labelPoint( QgsPoint& point, unsigned char* wkb);
129+
115130
/** Color to draw selected features */
116131
QColor mSelectionColor;
117132

0 commit comments

Comments
 (0)
Please sign in to comment.