Skip to content

Commit 17052b5

Browse files
author
wonder
committed
Added possibility to merge adjacent lines with the same label text so they're labeled only once.
git-svn-id: http://svn.osgeo.org/qgis/branches/symbology-ng-branch@11293 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 175e70a commit 17052b5

11 files changed

+209
-17
lines changed

src/core/pal/feature.cpp

+42-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ namespace pal
7777
FeaturePart::FeaturePart( Feature *feat, const GEOSGeometry* geom )
7878
: f(feat), nbHoles(0), holes(NULL)
7979
{
80+
// we'll remove const, but we won't modify that geometry
81+
the_geom = const_cast<GEOSGeometry*>(geom);
82+
ownsGeom = false; // geometry is owned by Feature class
83+
8084
extractCoords(geom);
8185

8286
holeOf = NULL;
@@ -98,6 +102,12 @@ namespace pal
98102
delete [] holes;
99103
holes = NULL;
100104
}
105+
106+
if (ownsGeom)
107+
{
108+
GEOSGeom_destroy(the_geom);
109+
the_geom = NULL;
110+
}
101111
}
102112

103113

@@ -581,7 +591,7 @@ void FeaturePart::removeDuplicatePoints()
581591
#endif
582592
if ( f->layer->arrangement == P_LINE )
583593
{
584-
std::cout << alpha*180/M_PI << std::endl;
594+
//std::cout << alpha*180/M_PI << std::endl;
585595
if ( flags & FLAG_MAP_ORIENTATION )
586596
reversed = ( alpha >= M_PI/2 || alpha < -M_PI/2 );
587597

@@ -1260,5 +1270,36 @@ void FeaturePart::removeDuplicatePoints()
12601270
}
12611271

12621272

1273+
bool FeaturePart::isConnected(FeaturePart* p2)
1274+
{
1275+
return (GEOSTouches(the_geom, p2->the_geom) == 1);
1276+
}
1277+
1278+
bool FeaturePart::mergeWithFeaturePart(FeaturePart* other)
1279+
{
1280+
GEOSGeometry* g1 = GEOSGeom_clone(the_geom);
1281+
GEOSGeometry* g2 = GEOSGeom_clone(other->the_geom);
1282+
GEOSGeometry* geoms[2] = { g1, g2 };
1283+
GEOSGeometry* g = GEOSGeom_createCollection(GEOS_MULTILINESTRING, geoms, 2);
1284+
GEOSGeometry* gTmp = GEOSLineMerge(g);
1285+
GEOSGeom_destroy(g);
1286+
1287+
if (GEOSGeomTypeId(gTmp) != GEOS_LINESTRING)
1288+
{
1289+
// sometimes it's not possible to merge lines (e.g. they don't touch at endpoints)
1290+
GEOSGeom_destroy(gTmp);
1291+
return false;
1292+
}
1293+
1294+
if (ownsGeom) // delete old geometry if we own it
1295+
GEOSGeom_destroy(the_geom);
1296+
// set up new geometry
1297+
the_geom = gTmp;
1298+
ownsGeom = true;
1299+
1300+
deleteCoords();
1301+
extractCoords(the_geom);
1302+
return true;
1303+
}
12631304

12641305
} // end namespace pal

src/core/pal/feature.h

+7
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ namespace pal
117117
PointSet **holes;
118118

119119
GEOSGeometry *the_geom;
120+
bool ownsGeom;
120121

121122
/** \brief read coordinates from a GEOS geom */
122123
void extractCoords( const GEOSGeometry* geom );
@@ -259,6 +260,12 @@ namespace pal
259260
int getNumSelfObstacles() const { return nbHoles; }
260261
PointSet* getSelfObstacle(int i) { return holes[i]; }
261262

263+
/** check whether this part is connected with some other part */
264+
bool isConnected(FeaturePart* p2);
265+
266+
/** merge other (connected) part with this one and save the result in this part (other is unchanged).
267+
* Return true on success, false if the feature wasn't modified */
268+
bool mergeWithFeaturePart(FeaturePart* other);
262269
};
263270

264271
} // end namespace pal

src/core/pal/layer.cpp

+107-5
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace pal
6262
: pal( pal ), obstacle( obstacle ), active( active ),
6363
toLabel( toLabel ), label_unit( label_unit ),
6464
min_scale( min_scale ), max_scale( max_scale ),
65-
arrangement( arrangement ), arrangementFlags( 0 ), mode(LabelPerFeature)
65+
arrangement( arrangement ), arrangementFlags( 0 ), mode(LabelPerFeature), mergeLines(false)
6666
{
6767

6868
this->name = new char[strlen( lyrName ) +1];
@@ -73,6 +73,9 @@ namespace pal
7373
rtree = new RTree<FeaturePart*, double, 2, double>();
7474
hashtable = new HashTable<Feature*> ( 5281 );
7575

76+
connectedHashtable = new HashTable< LinkedList<FeaturePart*>* > ( 5391 );
77+
connectedTexts = new LinkedList< char* >( strCompare );
78+
7679
if ( defaultPriority < 0.0001 )
7780
this->defaultPriority = 0.0001;
7881
else if ( defaultPriority > 1.0 )
@@ -95,8 +98,13 @@ namespace pal
9598
delete featureParts->pop_front();
9699
}
97100
delete featureParts;
101+
98102
}
99103

104+
// this hashtable and list should be empty if they still exist
105+
delete connectedHashtable;
106+
double connectedTexts;
107+
100108
// features in the hashtable
101109
if ( features )
102110
{
@@ -219,7 +227,7 @@ namespace pal
219227

220228

221229

222-
bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y )
230+
bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText )
223231
{
224232
if ( !geom_id || label_x < 0 || label_y < 0 )
225233
return false;
@@ -297,7 +305,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
297305
}
298306

299307
// feature part is ready!
300-
addFeaturePart(fpart);
308+
addFeaturePart(fpart, labelText);
301309

302310
first_feat = false;
303311
}
@@ -310,7 +318,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
310318
// if using only biggest parts...
311319
if (mode == LabelPerFeature && biggest_part != NULL)
312320
{
313-
addFeaturePart(biggest_part);
321+
addFeaturePart(biggest_part, labelText);
314322
first_feat = false;
315323
}
316324

@@ -328,7 +336,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
328336
return !first_feat; // true if we've added something
329337
}
330338

331-
void Layer::addFeaturePart( FeaturePart* fpart )
339+
void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
332340
{
333341
double bmin[2];
334342
double bmax[2];
@@ -339,6 +347,28 @@ void Layer::addFeaturePart( FeaturePart* fpart )
339347

340348
// add to r-tree for fast spatial access
341349
rtree->Insert( bmin, bmax, fpart );
350+
351+
// add to hashtable with equally named feature parts
352+
if (mergeLines && labelText)
353+
{
354+
LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find(labelText);
355+
LinkedList< FeaturePart*>* lst;
356+
if (lstPtr == NULL)
357+
{
358+
// entry doesn't exist yet
359+
lst = new LinkedList<FeaturePart*>( ptrFeaturePartCompare );
360+
connectedHashtable->insertItem(labelText, lst);
361+
362+
char* txt = new char[strlen(labelText) +1];
363+
strcpy(txt, labelText);
364+
connectedTexts->push_back(txt);
365+
}
366+
else
367+
{
368+
lst = *lstPtr;
369+
}
370+
lst->push_back(fpart); // add to the list
371+
}
342372
}
343373

344374

@@ -354,6 +384,78 @@ Units Layer::getLabelUnit()
354384
}
355385

356386

387+
static FeaturePart* _findConnectedPart(FeaturePart* partCheck, LinkedList<FeaturePart*>* otherParts)
388+
{
389+
// iterate in the rest of the parts with the same label
390+
Cell<FeaturePart*>* p = otherParts->getFirst();
391+
while (p)
392+
{
393+
if (partCheck->isConnected(p->item))
394+
{
395+
// stop checking for other connected parts
396+
return p->item;
397+
}
398+
p = p->next;
399+
}
400+
401+
return NULL; // no connected part found...
402+
}
403+
404+
void Layer::joinConnectedFeatures()
405+
{
406+
// go through all label texts
407+
char* labelText;
408+
while ( labelText = connectedTexts->pop_front() )
409+
{
410+
//std::cerr << "JOIN: " << labelText << std::endl;
411+
LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find(labelText);
412+
if (!partsPtr)
413+
continue; // shouldn't happen
414+
LinkedList<FeaturePart*>* parts = *partsPtr;
415+
416+
// go one-by-one part, try to merge
417+
while (parts->size())
418+
{
419+
// part we'll be checking against other in this round
420+
FeaturePart* partCheck = parts->pop_front();
421+
422+
FeaturePart* otherPart = _findConnectedPart(partCheck, parts);
423+
if (otherPart)
424+
{
425+
//std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
426+
427+
// remove partCheck from r-tree
428+
double bmin[2], bmax[2];
429+
partCheck->getBoundingBox(bmin, bmax);
430+
rtree->Remove(bmin,bmax, partCheck);
431+
432+
otherPart->getBoundingBox(bmin, bmax);
433+
434+
// merge points from partCheck to p->item
435+
if (otherPart->mergeWithFeaturePart(partCheck))
436+
{
437+
// reinsert p->item to r-tree (probably not needed)
438+
rtree->Remove(bmin,bmax, otherPart);
439+
otherPart->getBoundingBox(bmin, bmax);
440+
rtree->Insert(bmin, bmax, otherPart);
441+
}
442+
}
443+
}
444+
445+
// we're done processing feature parts with this particular label text
446+
delete parts;
447+
*partsPtr = NULL;
448+
delete labelText;
449+
}
450+
451+
// we're done processing connected fetures
452+
delete connectedHashtable;
453+
connectedHashtable = NULL;
454+
delete connectedTexts;
455+
connectedTexts = NULL;
456+
}
457+
458+
357459

358460
} // end namespace
359461

src/core/pal/layer.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace pal
101101
Arrangement arrangement;
102102

103103
LabelMode mode;
104+
bool mergeLines;
104105

105106
/** optional flags used for some placement methods */
106107
unsigned long arrangementFlags;
@@ -109,6 +110,9 @@ namespace pal
109110
RTree<FeaturePart*, double, 2, double, 8, 4> *rtree;
110111
HashTable<Feature*> *hashtable;
111112

113+
HashTable< LinkedList<FeaturePart*>* > * connectedHashtable;
114+
LinkedList< char* >* connectedTexts;
115+
112116
SimpleMutex *modMutex;
113117

114118
/**
@@ -140,7 +144,7 @@ namespace pal
140144
bool isScaleValid( double scale );
141145

142146
/** add newly creted feature part into r tree and to the list */
143-
void addFeaturePart( FeaturePart* fpart );
147+
void addFeaturePart( FeaturePart* fpart, const char* labelText = NULL );
144148

145149
public:
146150
/**
@@ -271,6 +275,9 @@ namespace pal
271275
void setLabelMode( LabelMode m ) { mode = m; }
272276
LabelMode getLabelMode() const { return mode; }
273277

278+
void setMergeConnectedLines(bool m) { mergeLines = m; }
279+
bool getMergeConnectedLines() const { return mergeLines; }
280+
274281
/**
275282
* \brief register a feature in the layer
276283
*
@@ -283,11 +290,14 @@ namespace pal
283290
*
284291
* @return true on success (i.e. valid geometry)
285292
*/
286-
bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1 );
293+
bool registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x = -1, double label_y = -1, const char* labelText = NULL );
287294

288295
/** return pointer to feature or NULL if doesn't exist */
289296
Feature* getFeature( const char* geom_id );
290297

298+
/** join connected features with the same label text */
299+
void joinConnectedFeatures();
300+
291301
};
292302

293303
} // end namespace pal

src/core/pal/pal.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ namespace pal
500500
// check if this selected layers has been selected by user
501501
if ( strcmp( layersName[i], layer->name ) == 0 )
502502
{
503+
// check for connected features with the same label text and join them
504+
if (layer->getMergeConnectedLines())
505+
layer->joinConnectedFeatures();
506+
503507
context->layer = layer;
504508
context->priority = layersFactor[i];
505509
// lookup for feature (and generates candidates list)

src/core/pal/pointset.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,24 @@ namespace pal
136136

137137
PointSet::~PointSet()
138138
{
139-
if ( x )
140-
delete[] x;
141-
if ( y )
142-
delete[] y;
139+
deleteCoords();
143140

144141
if ( status )
145142
delete[] status;
146143
if ( cHull )
147144
delete[] cHull;
148145
}
149146

147+
void PointSet::deleteCoords()
148+
{
149+
if ( x )
150+
delete[] x;
151+
if ( y )
152+
delete[] y;
153+
x = NULL;
154+
y = NULL;
155+
}
156+
150157

151158
int PointSet::getPath( int start, int stop, int *path_val )
152159
{

src/core/pal/pointset.h

+2
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ namespace pal
115115

116116
PointSet( PointSet &ps );
117117

118+
void deleteCoords();
119+
118120
double xmin;
119121
double xmax;
120122
double ymin;

src/plugins/labeling/labelinggui.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ LabelingGui::LabelingGui( PalLabeling* lbl, QgsVectorLayer* layer, QWidget* pare
6060
Q_ASSERT(0 && "NOOOO!");
6161
}
6262

63+
chkMergeLines->setEnabled(layer->geometryType() == QGis::Line);
64+
6365
populateFieldNames();
6466

6567
// load labeling settings from layer
@@ -111,6 +113,7 @@ LabelingGui::LabelingGui( PalLabeling* lbl, QgsVectorLayer* layer, QWidget* pare
111113
sliderPriority->setValue( lyr.priority );
112114
chkNoObstacle->setChecked( !lyr.obstacle );
113115
chkLabelPerFeaturePart->setChecked( lyr.labelPerPart );
116+
chkMergeLines->setChecked( lyr.mergeLines );
114117

115118
bool scaleBased = (lyr.scaleMin != 0 && lyr.scaleMax != 0);
116119
chkScaleBasedVisibility->setChecked(scaleBased);
@@ -207,6 +210,7 @@ LayerSettings LabelingGui::layerSettings()
207210
lyr.priority = sliderPriority->value();
208211
lyr.obstacle = !chkNoObstacle->isChecked();
209212
lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
213+
lyr.mergeLines = chkMergeLines->isChecked();
210214
if (chkScaleBasedVisibility->isChecked())
211215
{
212216
lyr.scaleMin = spinScaleMin->value();

0 commit comments

Comments
 (0)