std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary ) { // to store obstacles RTree<FeaturePart *, double, 2, double> *obstacles = new RTree<FeaturePart *, double, 2, double>(); std::unique_ptr< Problem > prob = qgis::make_unique< Problem >(); int i, j; double bbx[4]; double bby[4]; double amin[2]; double amax[2]; int max_p = 0; LabelPosition *lp = nullptr; bbx[0] = bbx[3] = amin[0] = prob->bbox[0] = extent.xMinimum(); bby[0] = bby[1] = amin[1] = prob->bbox[1] = extent.yMinimum(); bbx[1] = bbx[2] = amax[0] = prob->bbox[2] = extent.xMaximum(); bby[2] = bby[3] = amax[1] = prob->bbox[3] = extent.yMaximum(); prob->pal = this; QLinkedList<Feats *> *fFeats = new QLinkedList<Feats *>; FeatCallBackCtx context; // prepare map boundary geos::unique_ptr mapBoundaryGeos( QgsGeos::asGeos( mapBoundary ) ); geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r( QgsGeos::getGEOSHandler(), mapBoundaryGeos.get() ) ); context.fFeats = fFeats; context.obstacles = obstacles; context.candidates = prob->candidates; context.mapBoundary = mapBoundaryPrepared.get(); ObstacleCallBackCtx obstacleContext; obstacleContext.obstacles = obstacles; obstacleContext.obstacleCount = 0; // first step : extract features from layers int previousFeatureCount = 0; int previousObstacleCount = 0; QStringList layersWithFeaturesInBBox; mMutex.lock(); const auto constMLayers = mLayers; for ( Layer *layer : constMLayers ) { if ( !layer ) { // invalid layer name continue; } // only select those who are active if ( !layer->active() ) continue; // check for connected features with the same label text and join them if ( layer->mergeConnectedLines() ) layer->joinConnectedFeatures(); layer->chopFeaturesAtRepeatDistance(); layer->mMutex.lock(); // find features within bounding box and generate candidates list context.layer = layer; layer->mFeatureIndex->Search( amin, amax, extractFeatCallback, static_cast< void * >( &context ) ); // find obstacles within bounding box layer->mObstacleIndex->Search( amin, amax, extractObstaclesCallback, static_cast< void * >( &obstacleContext ) ); layer->mMutex.unlock(); if ( context.fFeats->size() - previousFeatureCount > 0 || obstacleContext.obstacleCount > previousObstacleCount ) { layersWithFeaturesInBBox << layer->name(); } previousFeatureCount = context.fFeats->size(); previousObstacleCount = obstacleContext.obstacleCount; } mMutex.unlock(); prob->nbLabelledLayers = layersWithFeaturesInBBox.size(); prob->labelledLayersName = layersWithFeaturesInBBox; if ( fFeats->isEmpty() ) { delete fFeats; delete obstacles; return nullptr; } prob->nbft = fFeats->size(); prob->nblp = 0; prob->featNbLp = new int [prob->nbft]; prob->featStartId = new int [prob->nbft]; prob->inactiveCost = new double[prob->nbft]; Feats *feat = nullptr; // Filtering label positions against obstacles amin[0] = amin[1] = std::numeric_limits<double>::lowest(); amax[0] = amax[1] = std::numeric_limits<double>::max(); FilterContext filterCtx; filterCtx.cdtsIndex = prob->candidates; filterCtx.pal = this; obstacles->Search( amin, amax, filteringCallback, static_cast< void * >( &filterCtx ) ); if ( isCanceled() ) { const auto constFFeats = *fFeats; for ( Feats *feat : constFFeats ) { qDeleteAll( feat->lPos ); feat->lPos.clear(); } qDeleteAll( *fFeats ); delete fFeats; delete obstacles; return nullptr; } int idlp = 0; for ( i = 0; i < prob->nbft; i++ ) /* foreach feature into prob */ { feat = fFeats->takeFirst(); prob->featStartId[i] = idlp; prob->inactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority ); switch ( feat->feature->getGeosType() ) { case GEOS_POINT: max_p = point_p; break; case GEOS_LINESTRING: max_p = line_p; break; case GEOS_POLYGON: max_p = poly_p; break; } // sort candidates by cost, skip less interesting ones, calculate polygon costs (if using polygons) max_p = CostCalculator::finalizeCandidatesCosts( feat, max_p, obstacles, bbx, bby ); // only keep the 'max_p' best candidates while ( feat->lPos.count() > max_p ) { // TODO remove from index feat->lPos.last()->removeFromIndex( prob->candidates ); delete feat->lPos.takeLast(); } // update problem's # candidate prob->featNbLp[i] = feat->lPos.count(); prob->nblp += feat->lPos.count(); // add all candidates into a rtree (to speed up conflicts searching) for ( j = 0; j < feat->lPos.count(); j++, idlp++ ) { lp = feat->lPos.at( j ); //lp->insertIntoIndex(prob->candidates); lp->setProblemIds( i, idlp ); // bugfix #1 (maxence 10/23/2008) } fFeats->append( feat ); } int nbOverlaps = 0; while ( !fFeats->isEmpty() ) // foreach feature { if ( isCanceled() ) { const auto constFFeats = *fFeats; for ( Feats *feat : constFFeats ) { qDeleteAll( feat->lPos ); feat->lPos.clear(); } qDeleteAll( *fFeats ); delete fFeats; delete obstacles; return nullptr; } feat = fFeats->takeFirst(); while ( !feat->lPos.isEmpty() ) // foreach label candidate { lp = feat->lPos.takeFirst(); lp->resetNumOverlaps(); // make sure that candidate's cost is less than 1 lp->validateCost(); prob->addCandidatePosition( lp ); //prob->feat[idlp] = j; lp->getBoundingBox( amin, amax ); // lookup for overlapping candidate prob->candidates->Search( amin, amax, LabelPosition::countOverlapCallback, static_cast< void * >( lp ) ); nbOverlaps += lp->getNumOverlaps(); } delete feat; } delete fFeats; //delete candidates; delete obstacles; nbOverlaps /= 2; prob->all_nblp = prob->nblp; prob->nbOverlap = nbOverlaps; return prob; }
/** * \Brief Problem Factory * Select features from user's choice layers within * a specific bounding box * param nbLayers # wanted layers * param layersFactor layers importance * param layersName layers in problem * param lambda_min west bbox * param phi_min south bbox * param lambda_max east bbox * param phi_max north bbox * param scale the scale */ Problem* Pal::extract( int nbLayers, char **layersName, double *layersFactor, double lambda_min, double phi_min, double lambda_max, double phi_max, double scale, std::ofstream *svgmap ) { // to store obstacles RTree<PointSet*, double, 2, double> *obstacles = new RTree<PointSet*, double, 2, double>(); Problem *prob = new Problem(); int i, j; double bbx[4]; double bby[4]; double amin[2]; double amax[2]; int max_p = 0; LabelPosition* lp; bbx[0] = bbx[3] = amin[0] = prob->bbox[0] = lambda_min; bby[0] = bby[1] = amin[1] = prob->bbox[1] = phi_min; bbx[1] = bbx[2] = amax[0] = prob->bbox[2] = lambda_max; bby[2] = bby[3] = amax[1] = prob->bbox[3] = phi_max; prob->scale = scale; prob->pal = this; LinkedList<Feats*> *fFeats = new LinkedList<Feats*> ( ptrFeatsCompare ); FeatCallBackCtx *context = new FeatCallBackCtx(); context->fFeats = fFeats; context->scale = scale; context->obstacles = obstacles; context->candidates = prob->candidates; context->bbox_min[0] = amin[0]; context->bbox_min[1] = amin[1]; context->bbox_max[0] = amax[0]; context->bbox_max[1] = amax[1]; #ifdef _EXPORT_MAP_ context->svgmap = svgmap; #endif #ifdef _VERBOSE_ std::cout << nbLayers << "/" << layers->size() << " layers to extract " << std::endl; std::cout << "scale is 1:" << scale << std::endl << std::endl; #endif /* First step : extract feature from layers * * */ int oldNbft = 0; Layer *layer; std::list<char*> *labLayers = new std::list<char*>(); lyrsMutex->lock(); for ( i = 0; i < nbLayers; i++ ) { for ( std::list<Layer*>::iterator it = layers->begin(); it != layers->end(); it++ ) // iterate on pal->layers { layer = *it; // Only select those who are active and labellable (with scale constraint) or those who are active and which must be treated as obstaclewhich must be treated as obstacle if ( layer->active && ( layer->obstacle || ( layer->toLabel && layer->isScaleValid( scale ) ) ) ) { // 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) #ifdef _EXPORT_MAP_ *svgmap << "<g inkscape:label=\"" << layer->name << "\"" << std::endl << " inkscape:groupmode=\"layer\"" << std::endl << " id=\"" << layer->name << "\">" << std::endl << std::endl; #endif context->layer->modMutex->lock(); context->layer->rtree->Search( amin, amax, extractFeatCallback, ( void* ) context ); context->layer->modMutex->unlock(); #ifdef _EXPORT_MAP_ *svgmap << "</g>" << std::endl << std::endl; #endif #ifdef _VERBOSE_ std::cout << "Layer's name: " << layer->getName() << std::endl; std::cout << " scale range: " << layer->getMinScale() << "->" << layer->getMaxScale() << std::endl; std::cout << " active:" << layer->isToLabel() << std::endl; std::cout << " obstacle:" << layer->isObstacle() << std::endl; std::cout << " toLabel:" << layer->isToLabel() << std::endl; std::cout << " # features: " << layer->getNbFeatures() << std::endl; std::cout << " # extracted features: " << context->fFeats->size() - oldNbft << std::endl; #endif if ( context->fFeats->size() - oldNbft > 0 ) { char *name = new char[strlen( layer->getName() ) +1]; strcpy( name, layer->getName() ); labLayers->push_back( name ); } oldNbft = context->fFeats->size(); break; } } } } delete context; lyrsMutex->unlock(); prob->nbLabelledLayers = labLayers->size(); prob->labelledLayersName = new char*[prob->nbLabelledLayers]; for ( i = 0; i < prob->nbLabelledLayers; i++ ) { prob->labelledLayersName[i] = labLayers->front(); labLayers->pop_front(); } delete labLayers; if ( fFeats->size() == 0 ) { #ifdef _VERBOSE_ std::cout << std::endl << "Empty problem" << std::endl; #endif delete fFeats; delete prob; delete obstacles; return NULL; } prob->nbft = fFeats->size(); prob->nblp = 0; prob->featNbLp = new int [prob->nbft]; prob->featStartId = new int [prob->nbft]; prob->inactiveCost = new double[prob->nbft]; Feats *feat; #ifdef _VERBOSE_ std::cout << "FIRST NBFT : " << prob->nbft << std::endl; #endif // Filtering label positions against obstacles amin[0] = amin[1] = -DBL_MAX; amax[0] = amax[1] = DBL_MAX; FilterContext filterCtx; filterCtx.cdtsIndex = prob->candidates; filterCtx.scale = prob->scale; filterCtx.pal = this; obstacles->Search( amin, amax, filteringCallback, ( void* ) &filterCtx ); int idlp = 0; for ( i = 0; i < prob->nbft; i++ ) /* foreach feature into prob */ { feat = fFeats->pop_front(); #ifdef _DEBUG_FULL_ std::cout << "Feature:" << feat->feature->layer->name << "/" << feat->feature->uid << std::endl; #endif prob->featStartId[i] = idlp; prob->inactiveCost[i] = pow( 2, 10 - 10 * feat->priority ); switch ( feat->feature->getGeosType() ) { case GEOS_POINT: max_p = point_p; break; case GEOS_LINESTRING: max_p = line_p; break; case GEOS_POLYGON: max_p = poly_p; break; } // sort candidates by cost, skip less interesting ones, calculate polygon costs (if using polygons) max_p = CostCalculator::finalizeCandidatesCosts( feat, max_p, obstacles, bbx, bby ); #ifdef _DEBUG_FULL_ std::cout << "All Cost are setted" << std::endl; #endif // only keep the 'max_p' best candidates for ( j = max_p; j < feat->nblp; j++ ) { // TODO remove from index feat->lPos[j]->removeFromIndex( prob->candidates ); delete feat->lPos[j]; } feat->nblp = max_p; // update problem's # candidate prob->featNbLp[i] = feat->nblp; prob->nblp += feat->nblp; // add all candidates into a rtree (to speed up conflicts searching) for ( j = 0; j < feat->nblp; j++, idlp++ ) { lp = feat->lPos[j]; //lp->insertIntoIndex(prob->candidates); lp->setProblemIds( i, idlp ); // bugfix #1 (maxence 10/23/2008) } fFeats->push_back( feat ); } #ifdef _DEBUG_FULL_ std::cout << "Malloc problem...." << std::endl; #endif idlp = 0; int nbOverlaps = 0; prob->labelpositions = new LabelPosition*[prob->nblp]; //prob->feat = new int[prob->nblp]; #ifdef _DEBUG_FULL_ std::cout << "problem malloc'd" << std::endl; #endif j = 0; while ( fFeats->size() > 0 ) // foreach feature { feat = fFeats->pop_front(); for ( i = 0; i < feat->nblp; i++, idlp++ ) // foreach label candidate { lp = feat->lPos[i]; lp->resetNumOverlaps(); // make sure that candidate's cost is less than 1 lp->validateCost(); prob->labelpositions[idlp] = lp; //prob->feat[idlp] = j; lp->getBoundingBox( amin, amax ); // lookup for overlapping candidate prob->candidates->Search( amin, amax, LabelPosition::countOverlapCallback, ( void* ) lp ); nbOverlaps += lp->getNumOverlaps(); #ifdef _DEBUG_FULL_ std::cout << "Nb overlap for " << idlp << "/" << prob->nblp - 1 << " : " << lp->nbOverlap << std::endl; #endif } j++; delete[] feat->lPos; delete feat; } delete fFeats; //delete candidates; delete obstacles; nbOverlaps /= 2; prob->all_nblp = prob->nblp; prob->nbOverlap = nbOverlaps; #ifdef _VERBOSE_ std::cout << "nbOverlap: " << prob->nbOverlap << std::endl; std::cerr << scale << "\t" << prob->nbft << "\t" << prob->nblp << "\t" << prob->nbOverlap << "\t"; #endif return prob; }