Skip to content

Commit

Permalink
Added possibility to merge adjacent lines with the same label text so…
Browse files Browse the repository at this point in the history
… they're labeled only once.

git-svn-id: http://svn.osgeo.org/qgis/branches/symbology-ng-branch@11293 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
wonder committed Aug 8, 2009
1 parent 175e70a commit 17052b5
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 17 deletions.
43 changes: 42 additions & 1 deletion src/core/pal/feature.cpp
Expand Up @@ -77,6 +77,10 @@ namespace pal
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;
Expand All @@ -98,6 +102,12 @@ namespace pal
delete [] holes;
holes = NULL;
}

if (ownsGeom)
{
GEOSGeom_destroy(the_geom);
the_geom = NULL;
}
}


Expand Down Expand Up @@ -581,7 +591,7 @@ void FeaturePart::removeDuplicatePoints()
#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 );

Expand Down Expand Up @@ -1260,5 +1270,36 @@ void FeaturePart::removeDuplicatePoints()
}


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
7 changes: 7 additions & 0 deletions src/core/pal/feature.h
Expand Up @@ -117,6 +117,7 @@ namespace pal
PointSet **holes;

GEOSGeometry *the_geom;
bool ownsGeom;

/** \brief read coordinates from a GEOS geom */
void extractCoords( const GEOSGeometry* geom );
Expand Down Expand Up @@ -259,6 +260,12 @@ namespace pal
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
Expand Down
112 changes: 107 additions & 5 deletions src/core/pal/layer.cpp
Expand Up @@ -62,7 +62,7 @@ namespace pal
: 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];
Expand All @@ -73,6 +73,9 @@ namespace pal
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 )
Expand All @@ -95,8 +98,13 @@ namespace pal
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 )
{
Expand Down Expand Up @@ -219,7 +227,7 @@ namespace pal



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;
Expand Down Expand Up @@ -297,7 +305,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
}

// feature part is ready!
addFeaturePart(fpart);
addFeaturePart(fpart, labelText);

first_feat = false;
}
Expand All @@ -310,7 +318,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
// if using only biggest parts...
if (mode == LabelPerFeature && biggest_part != NULL)
{
addFeaturePart(biggest_part);
addFeaturePart(biggest_part, labelText);
first_feat = false;
}

Expand All @@ -328,7 +336,7 @@ bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double
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];
Expand All @@ -339,6 +347,28 @@ void Layer::addFeaturePart( FeaturePart* fpart )

// 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
}
}


Expand All @@ -354,6 +384,78 @@ Units Layer::getLabelUnit()
}


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

14 changes: 12 additions & 2 deletions src/core/pal/layer.h
Expand Up @@ -101,6 +101,7 @@ namespace pal
Arrangement arrangement;

LabelMode mode;
bool mergeLines;

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

HashTable< LinkedList<FeaturePart*>* > * connectedHashtable;
LinkedList< char* >* connectedTexts;

SimpleMutex *modMutex;

/**
Expand Down Expand Up @@ -140,7 +144,7 @@ namespace pal
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:
/**
Expand Down Expand Up @@ -271,6 +275,9 @@ namespace pal
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
*
Expand All @@ -283,11 +290,14 @@ namespace pal
*
* @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
Expand Down
4 changes: 4 additions & 0 deletions src/core/pal/pal.cpp
Expand Up @@ -500,6 +500,10 @@ namespace pal
// 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)
Expand Down
15 changes: 11 additions & 4 deletions src/core/pal/pointset.cpp
Expand Up @@ -136,17 +136,24 @@ namespace pal

PointSet::~PointSet()
{
if ( x )
delete[] x;
if ( y )
delete[] y;
deleteCoords();

if ( status )
delete[] status;
if ( cHull )
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 )
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/pal/pointset.h
Expand Up @@ -115,6 +115,8 @@ namespace pal

PointSet( PointSet &ps );

void deleteCoords();

double xmin;
double xmax;
double ymin;
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/labeling/labelinggui.cpp
Expand Up @@ -60,6 +60,8 @@ LabelingGui::LabelingGui( PalLabeling* lbl, QgsVectorLayer* layer, QWidget* pare
Q_ASSERT(0 && "NOOOO!");
}

chkMergeLines->setEnabled(layer->geometryType() == QGis::Line);

populateFieldNames();

// load labeling settings from layer
Expand Down Expand Up @@ -111,6 +113,7 @@ LabelingGui::LabelingGui( PalLabeling* lbl, QgsVectorLayer* layer, QWidget* pare
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);
Expand Down Expand Up @@ -207,6 +210,7 @@ LayerSettings LabelingGui::layerSettings()
lyr.priority = sliderPriority->value();
lyr.obstacle = !chkNoObstacle->isChecked();
lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
lyr.mergeLines = chkMergeLines->isChecked();
if (chkScaleBasedVisibility->isChecked())
{
lyr.scaleMin = spinScaleMin->value();
Expand Down

0 comments on commit 17052b5

Please sign in to comment.