mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
Chop lines after merging connected features
This commit is contained in:
parent
7e4600fed6
commit
00692c7fd8
@ -228,6 +228,18 @@ namespace pal
|
||||
*/
|
||||
//int getId();
|
||||
|
||||
/**
|
||||
* \brief return the feature
|
||||
* \return the feature
|
||||
*/
|
||||
Feature* getFeature() { return f; }
|
||||
|
||||
/**
|
||||
* \brief return the geometry
|
||||
* \return the geometry
|
||||
*/
|
||||
const GEOSGeometry* getGeometry() const { return the_geom; }
|
||||
|
||||
/**
|
||||
* \brief return the layer that feature belongs to
|
||||
* \return the layer of the feature
|
||||
|
@ -229,7 +229,7 @@ namespace pal
|
||||
|
||||
bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText,
|
||||
double labelPosX, double labelPosY, bool fixedPos, double angle, bool fixedAngle,
|
||||
int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow, double repeatDistance )
|
||||
int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow )
|
||||
{
|
||||
if ( !geom_id || label_x < 0 || label_y < 0 )
|
||||
return false;
|
||||
@ -285,86 +285,6 @@ namespace pal
|
||||
throw InternalException::UnknownGeometry();
|
||||
}
|
||||
|
||||
// if multiple labels are requested for lines, split the line in pieces of desired distance
|
||||
if ( repeatDistance > 0 )
|
||||
{
|
||||
int nSimpleGeometries = simpleGeometries->size();
|
||||
for ( int i = 0; i < nSimpleGeometries; ++i )
|
||||
{
|
||||
const GEOSGeometry* geom = simpleGeometries->pop_front();
|
||||
if ( GEOSGeomTypeId( geom ) == GEOS_LINESTRING )
|
||||
{
|
||||
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq( geom );
|
||||
|
||||
// get number of points
|
||||
unsigned int n;
|
||||
GEOSCoordSeq_getSize( cs, &n );
|
||||
|
||||
// Read points
|
||||
std::vector<Point> points( n );
|
||||
for ( unsigned int i = 0; i < n; ++i )
|
||||
{
|
||||
GEOSCoordSeq_getX( cs, i, &points[i].x );
|
||||
GEOSCoordSeq_getY( cs, i, &points[i].y );
|
||||
}
|
||||
|
||||
// Cumulative length vector
|
||||
std::vector<double> len( n, 0 );
|
||||
for ( unsigned int i = 1; i < n; ++i )
|
||||
{
|
||||
double dx = points[i].x - points[i - 1].x;
|
||||
double dy = points[i].y - points[i - 1].y;
|
||||
len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
|
||||
}
|
||||
|
||||
// Walk along line
|
||||
unsigned int cur = 0;
|
||||
double lambda = 0;
|
||||
std::vector<Point> part;
|
||||
for ( ;; )
|
||||
{
|
||||
lambda += repeatDistance;
|
||||
for ( ; cur < n && lambda > len[cur]; ++cur )
|
||||
{
|
||||
part.push_back( points[cur] );
|
||||
}
|
||||
if ( cur >= n )
|
||||
{
|
||||
break;
|
||||
}
|
||||
double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
|
||||
Point p;
|
||||
p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
|
||||
p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
|
||||
part.push_back( p );
|
||||
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
|
||||
for ( std::size_t i = 0; i < part.size(); ++i )
|
||||
{
|
||||
GEOSCoordSeq_setX( cooSeq, i, part[i].x );
|
||||
GEOSCoordSeq_setY( cooSeq, i, part[i].y );
|
||||
}
|
||||
|
||||
simpleGeometries->push_back( GEOSGeom_createLineString( cooSeq ) );
|
||||
part.clear();
|
||||
part.push_back( p );
|
||||
}
|
||||
// Create final part
|
||||
part.push_back( points[n - 1] );
|
||||
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
|
||||
for ( std::size_t i = 0; i < part.size(); ++i )
|
||||
{
|
||||
GEOSCoordSeq_setX( cooSeq, i, part[i].x );
|
||||
GEOSCoordSeq_setY( cooSeq, i, part[i].y );
|
||||
}
|
||||
simpleGeometries->push_back( GEOSGeom_createLineString( cooSeq ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
simpleGeometries->push_back( geom );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ( simpleGeometries->size() > 0 )
|
||||
{
|
||||
const GEOSGeometry* geom = simpleGeometries->pop_front();
|
||||
@ -401,7 +321,7 @@ namespace pal
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( mode == LabelPerFeature && repeatDistance == 0.0 && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
|
||||
if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
|
||||
{
|
||||
if ( type == GEOS_LINESTRING )
|
||||
GEOSLength( geom, &geom_size );
|
||||
@ -430,7 +350,7 @@ namespace pal
|
||||
modMutex->unlock();
|
||||
|
||||
// if using only biggest parts...
|
||||
if ((( mode == LabelPerFeature && repeatDistance == 0.0 ) || f->fixedPosition() ) && biggest_part != NULL )
|
||||
if (( mode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL )
|
||||
{
|
||||
addFeaturePart( biggest_part, labelText );
|
||||
first_feat = false;
|
||||
@ -569,6 +489,102 @@ namespace pal
|
||||
connectedTexts = NULL;
|
||||
}
|
||||
|
||||
void Layer::chopFeatures( double chopInterval )
|
||||
{
|
||||
LinkedList<FeaturePart*> * newFeatureParts = new LinkedList<FeaturePart*>( ptrFeaturePartCompare );
|
||||
while ( FeaturePart* fpart = featureParts->pop_front() )
|
||||
{
|
||||
const GEOSGeometry* geom = fpart->getGeometry();
|
||||
if ( GEOSGeomTypeId( geom ) == GEOS_LINESTRING )
|
||||
{
|
||||
|
||||
double bmin[2], bmax[2];
|
||||
fpart->getBoundingBox( bmin, bmax );
|
||||
rtree->Remove( bmin, bmax, fpart );
|
||||
|
||||
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq( geom );
|
||||
|
||||
// get number of points
|
||||
unsigned int n;
|
||||
GEOSCoordSeq_getSize( cs, &n );
|
||||
|
||||
// Read points
|
||||
std::vector<Point> points( n );
|
||||
for ( unsigned int i = 0; i < n; ++i )
|
||||
{
|
||||
GEOSCoordSeq_getX( cs, i, &points[i].x );
|
||||
GEOSCoordSeq_getY( cs, i, &points[i].y );
|
||||
}
|
||||
|
||||
// Cumulative length vector
|
||||
std::vector<double> len( n, 0 );
|
||||
for ( unsigned int i = 1; i < n; ++i )
|
||||
{
|
||||
double dx = points[i].x - points[i - 1].x;
|
||||
double dy = points[i].y - points[i - 1].y;
|
||||
len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
|
||||
}
|
||||
|
||||
// Walk along line
|
||||
unsigned int cur = 0;
|
||||
double lambda = 0;
|
||||
std::vector<Point> part;
|
||||
for ( ;; )
|
||||
{
|
||||
lambda += chopInterval;
|
||||
for ( ; cur < n && lambda > len[cur]; ++cur )
|
||||
{
|
||||
part.push_back( points[cur] );
|
||||
}
|
||||
if ( cur >= n )
|
||||
{
|
||||
break;
|
||||
}
|
||||
double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
|
||||
Point p;
|
||||
p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
|
||||
p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
|
||||
part.push_back( p );
|
||||
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
|
||||
for ( std::size_t i = 0; i < part.size(); ++i )
|
||||
{
|
||||
GEOSCoordSeq_setX( cooSeq, i, part[i].x );
|
||||
GEOSCoordSeq_setY( cooSeq, i, part[i].y );
|
||||
}
|
||||
|
||||
GEOSGeometry* newgeom = GEOSGeom_createLineString( cooSeq );
|
||||
FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
|
||||
newFeatureParts->push_back( newfpart );
|
||||
newfpart->getBoundingBox( bmin, bmax );
|
||||
rtree->Insert( bmin, bmax, newfpart );
|
||||
part.clear();
|
||||
part.push_back( p );
|
||||
}
|
||||
// Create final part
|
||||
part.push_back( points[n - 1] );
|
||||
GEOSCoordSequence* cooSeq = GEOSCoordSeq_create( part.size(), 2 );
|
||||
for ( std::size_t i = 0; i < part.size(); ++i )
|
||||
{
|
||||
GEOSCoordSeq_setX( cooSeq, i, part[i].x );
|
||||
GEOSCoordSeq_setY( cooSeq, i, part[i].y );
|
||||
}
|
||||
|
||||
GEOSGeometry* newgeom = GEOSGeom_createLineString( cooSeq );
|
||||
FeaturePart* newfpart = new FeaturePart( fpart->getFeature(), newgeom );
|
||||
newFeatureParts->push_back( newfpart );
|
||||
newfpart->getBoundingBox( bmin, bmax );
|
||||
rtree->Insert( bmin, bmax, newfpart );
|
||||
}
|
||||
else
|
||||
{
|
||||
newFeatureParts->push_back( fpart );
|
||||
}
|
||||
}
|
||||
|
||||
delete featureParts;
|
||||
featureParts = newFeatureParts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // end namespace
|
||||
|
@ -113,6 +113,7 @@ namespace pal
|
||||
unsigned long arrangementFlags;
|
||||
LabelMode mode;
|
||||
bool mergeLines;
|
||||
double repeatDistance;
|
||||
|
||||
UpsideDownLabels upsidedownLabels;
|
||||
|
||||
@ -289,6 +290,9 @@ namespace pal
|
||||
void setMergeConnectedLines( bool m ) { mergeLines = m; }
|
||||
bool getMergeConnectedLines() const { return mergeLines; }
|
||||
|
||||
void setRepeatDistance( double distance ) { repeatDistance = distance; }
|
||||
double getRepeatDistance() const { return repeatDistance; }
|
||||
|
||||
void setUpsidedownLabels( UpsideDownLabels ud ) { upsidedownLabels = ud; }
|
||||
UpsideDownLabels getUpsidedownLabels() const { return upsidedownLabels; }
|
||||
|
||||
@ -321,7 +325,7 @@ namespace pal
|
||||
const char* labelText = NULL, double labelPosX = 0.0, double labelPosY = 0.0,
|
||||
bool fixedPos = false, double angle = 0.0, bool fixedAngle = false,
|
||||
int xQuadOffset = 0, int yQuadOffset = 0, double xOffset = 0.0, double yOffset = 0.0,
|
||||
bool alwaysShow = false, double repeatDistance = 0.0 );
|
||||
bool alwaysShow = false );
|
||||
|
||||
/** return pointer to feature or NULL if doesn't exist */
|
||||
Feature* getFeature( const char* geom_id );
|
||||
@ -329,6 +333,8 @@ namespace pal
|
||||
/** join connected features with the same label text */
|
||||
void joinConnectedFeatures();
|
||||
|
||||
void chopFeatures(double chopInterval );
|
||||
|
||||
};
|
||||
|
||||
} // end namespace pal
|
||||
|
@ -429,6 +429,9 @@ namespace pal
|
||||
if ( layer->getMergeConnectedLines() )
|
||||
layer->joinConnectedFeatures();
|
||||
|
||||
if ( layer->getRepeatDistance() > 0 )
|
||||
layer->chopFeatures( layer->getRepeatDistance() );
|
||||
|
||||
context->layer = layer;
|
||||
context->priority = layersFactor[i];
|
||||
// lookup for feature (and generates candidates list)
|
||||
|
@ -2264,48 +2264,12 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
|
||||
#endif
|
||||
lbl->setDefinedFont( labelFont );
|
||||
|
||||
// data defined repeat distance?
|
||||
double repeatDist = repeatDistance;
|
||||
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistance, exprVal ) )
|
||||
{
|
||||
bool ok;
|
||||
double distD = exprVal.toDouble( &ok );
|
||||
if ( ok )
|
||||
{
|
||||
repeatDist = distD;
|
||||
}
|
||||
}
|
||||
|
||||
// data defined label-repeat distance units?
|
||||
bool repeatdistinmapunit = repeatDistanceUnit == MapUnits;
|
||||
if ( dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistanceUnit, exprVal ) )
|
||||
{
|
||||
QString units = exprVal.toString().trimmed();
|
||||
QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
|
||||
if ( !units.isEmpty() )
|
||||
{
|
||||
repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
|
||||
}
|
||||
}
|
||||
|
||||
if ( repeatDist != 0 )
|
||||
{
|
||||
if ( !repeatdistinmapunit ) //convert distance from mm/map units to pixels
|
||||
{
|
||||
repeatDist *= repeatDistanceMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
|
||||
}
|
||||
else //mm
|
||||
{
|
||||
repeatDist *= vectorScaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
// feature to the layer
|
||||
try
|
||||
{
|
||||
if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
|
||||
xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
|
||||
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
|
||||
quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow ) )
|
||||
return;
|
||||
}
|
||||
catch ( std::exception &e )
|
||||
@ -3422,9 +3386,49 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
|
||||
// set whether adjacent lines should be merged
|
||||
l->setMergeConnectedLines( lyr.mergeLines );
|
||||
|
||||
|
||||
// set whether location of centroid must be inside of polygons
|
||||
l->setCentroidInside( lyr.centroidInside );
|
||||
|
||||
// set repeat distance
|
||||
// data defined repeat distance?
|
||||
QVariant exprVal;
|
||||
double repeatDist = lyr.repeatDistance;
|
||||
if ( lyr.dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistance, exprVal ) )
|
||||
{
|
||||
bool ok;
|
||||
double distD = exprVal.toDouble( &ok );
|
||||
if ( ok )
|
||||
{
|
||||
repeatDist = distD;
|
||||
}
|
||||
}
|
||||
|
||||
// data defined label-repeat distance units?
|
||||
bool repeatdistinmapunit = lyr.repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
|
||||
if ( lyr.dataDefinedEvaluate( QgsPalLayerSettings::RepeatDistanceUnit, exprVal ) )
|
||||
{
|
||||
QString units = exprVal.toString().trimmed();
|
||||
QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
|
||||
if ( !units.isEmpty() )
|
||||
{
|
||||
repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
|
||||
}
|
||||
}
|
||||
|
||||
if ( repeatDist != 0 )
|
||||
{
|
||||
if ( !repeatdistinmapunit ) //convert distance from mm/map units to pixels
|
||||
{
|
||||
repeatDist *= lyr.repeatDistanceMapUnitScale.computeMapUnitsPerPixel( ctx ) * ctx.scaleFactor();
|
||||
}
|
||||
else //mm
|
||||
{
|
||||
repeatDist *= lyr.vectorScaleFactor;
|
||||
}
|
||||
}
|
||||
l->setRepeatDistance( repeatDist );
|
||||
|
||||
// set how to show upside-down labels
|
||||
Layer::UpsideDownLabels upsdnlabels;
|
||||
switch ( lyr.upsidedownLabels )
|
||||
|
Loading…
x
Reference in New Issue
Block a user