int main () { QLinkedList<QString> list; list << "A" << "B" << "C" << "D" << "E" << "F"; assert(list.takeFirst() == "A"); assert(list.size() == 5); return 0; }
void Layer::joinConnectedFeatures() { // go through all label texts QString labelText; while ( !connectedTexts->isEmpty() ) { labelText = connectedTexts->takeFirst(); //std::cerr << "JOIN: " << labelText << std::endl; QHash< QString, QLinkedList<FeaturePart*>* >::const_iterator partsPtr = connectedHashtable->find( labelText ); if ( partsPtr == connectedHashtable->constEnd() ) continue; // shouldn't happen QLinkedList<FeaturePart*>* parts = *partsPtr; // go one-by-one part, try to merge while ( !parts->isEmpty() ) { // part we'll be checking against other in this round FeaturePart* partCheck = parts->takeFirst(); 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 ); featureParts->removeOne( 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; } // we're done processing connected fetures delete connectedHashtable; connectedHashtable = NULL; delete connectedTexts; connectedTexts = NULL; }
QLinkedList<const GEOSGeometry *>* pal::Util::unmulti( const GEOSGeometry *the_geom ) { QLinkedList<const GEOSGeometry*> *queue = new QLinkedList<const GEOSGeometry*>; QLinkedList<const GEOSGeometry*> *final_queue = new QLinkedList<const GEOSGeometry*>; const GEOSGeometry *geom; queue->append( the_geom ); int nGeom; int i; GEOSContextHandle_t geosctxt = geosContext(); while ( !queue->isEmpty() ) { geom = queue->takeFirst(); int type = GEOSGeomTypeId_r( geosctxt, geom ); switch ( type ) { case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: nGeom = GEOSGetNumGeometries_r( geosctxt, geom ); for ( i = 0; i < nGeom; i++ ) { queue->append( GEOSGetGeometryN_r( geosctxt, geom, i ) ); } break; case GEOS_POINT: case GEOS_LINESTRING: case GEOS_POLYGON: final_queue->append( geom ); break; default: QgsDebugMsg( QString( "unexpected geometry type:%1" ).arg( type ) ); delete final_queue; delete queue; return nullptr; } } delete queue; return final_queue; }
QLinkedList<const GEOSGeometry *> *unmulti( const GEOSGeometry *the_geom ) { QLinkedList<const GEOSGeometry*> *queue = new QLinkedList<const GEOSGeometry*>; QLinkedList<const GEOSGeometry*> *final_queue = new QLinkedList<const GEOSGeometry*>; const GEOSGeometry *geom; queue->append( the_geom ); int nGeom; int i; while ( !queue->isEmpty() ) { geom = queue->takeFirst(); GEOSContextHandle_t geosctxt = geosContext(); switch ( GEOSGeomTypeId_r( geosctxt, geom ) ) { case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: nGeom = GEOSGetNumGeometries_r( geosctxt, geom ); for ( i = 0; i < nGeom; i++ ) { queue->append( GEOSGetGeometryN_r( geosctxt, geom, i ) ); } break; case GEOS_POINT: case GEOS_LINESTRING: case GEOS_POLYGON: final_queue->append( geom ); break; default: delete final_queue; delete queue; return NULL; } } delete queue; return final_queue; }
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; }
bool Layer::registerFeature( QgsLabelFeature* lf ) { if ( lf->size().width() < 0 || lf->size().height() < 0 ) return false; mMutex.lock(); if ( mHashtable.contains( lf->id() ) ) { mMutex.unlock(); //A feature with this id already exists. Don't throw an exception as sometimes, //the same feature is added twice (dateline split with otf-reprojection) return false; } // assign label feature to this PAL layer lf->setLayer( this ); // Split MULTI GEOM and Collection in simple geometries bool addedFeature = false; double geom_size = -1, biggest_size = -1; FeaturePart* biggest_part = nullptr; // break the (possibly multi-part) geometry into simple geometries QLinkedList<const GEOSGeometry*>* simpleGeometries = Util::unmulti( lf->geometry() ); if ( !simpleGeometries ) // unmulti() failed? { mMutex.unlock(); throw InternalException::UnknownGeometry(); } GEOSContextHandle_t geosctxt = geosContext(); bool featureGeomIsObstacleGeom = !lf->obstacleGeometry(); while ( !simpleGeometries->isEmpty() ) { const GEOSGeometry* geom = simpleGeometries->takeFirst(); // ignore invalid geometries (e.g. polygons with self-intersecting rings) if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception { continue; } int type = GEOSGeomTypeId_r( geosctxt, geom ); if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON ) { mMutex.unlock(); throw InternalException::UnknownGeometry(); } FeaturePart* fpart = new FeaturePart( lf, geom ); // ignore invalid geometries if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) || ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) ) { delete fpart; continue; } // polygons: reorder coordinates if ( type == GEOS_POLYGON && GeomFunction::reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 ) { delete fpart; continue; } // is the feature well defined? TODO Check epsilon bool labelWellDefined = ( lf->size().width() > 0.0000001 && lf->size().height() > 0.0000001 ); if ( lf->isObstacle() && featureGeomIsObstacleGeom ) { //if we are not labelling the layer, only insert it into the obstacle list and avoid an //unnecessary copy if ( mLabelLayer && labelWellDefined ) { addObstaclePart( new FeaturePart( *fpart ) ); } else { addObstaclePart( fpart ); fpart = nullptr; } } // feature has to be labeled? if ( !mLabelLayer || !labelWellDefined ) { //nothing more to do for this part delete fpart; continue; } if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) ) { if ( type == GEOS_LINESTRING ) GEOSLength_r( geosctxt, geom, &geom_size ); else if ( type == GEOS_POLYGON ) GEOSArea_r( geosctxt, geom, &geom_size ); if ( geom_size > biggest_size ) { biggest_size = geom_size; delete biggest_part; // safe with NULL part biggest_part = fpart; } else { delete fpart; } continue; // don't add the feature part now, do it later } // feature part is ready! addFeaturePart( fpart, lf->labelText() ); addedFeature = true; } delete simpleGeometries; if ( !featureGeomIsObstacleGeom ) { //do the same for the obstacle geometry simpleGeometries = Util::unmulti( lf->obstacleGeometry() ); if ( !simpleGeometries ) // unmulti() failed? { mMutex.unlock(); throw InternalException::UnknownGeometry(); } while ( !simpleGeometries->isEmpty() ) { const GEOSGeometry* geom = simpleGeometries->takeFirst(); // ignore invalid geometries (e.g. polygons with self-intersecting rings) if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception { continue; } int type = GEOSGeomTypeId_r( geosctxt, geom ); if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON ) { mMutex.unlock(); throw InternalException::UnknownGeometry(); } FeaturePart* fpart = new FeaturePart( lf, geom ); // ignore invalid geometries if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) || ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) ) { delete fpart; continue; } // polygons: reorder coordinates if ( type == GEOS_POLYGON && GeomFunction::reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 ) { delete fpart; continue; } // feature part is ready! addObstaclePart( fpart ); } delete simpleGeometries; } mMutex.unlock(); // if using only biggest parts... if (( mMode == LabelPerFeature || lf->hasFixedPosition() ) && biggest_part ) { addFeaturePart( biggest_part, lf->labelText() ); addedFeature = true; } // add feature to layer if we have added something if ( addedFeature ) { mHashtable.insert( lf->id(), lf ); } return addedFeature; // true if we've added something }
void Layer::joinConnectedFeatures() { // go through all label texts int connectedFeaturesId = 0; Q_FOREACH ( const QString& labelText, mConnectedTexts ) { if ( !mConnectedHashtable.contains( labelText ) ) continue; // shouldn't happen connectedFeaturesId++; QLinkedList<FeaturePart*>* parts = mConnectedHashtable.value( labelText ); // go one-by-one part, try to merge while ( !parts->isEmpty() && parts->count() > 1 ) { // part we'll be checking against other in this round FeaturePart* partCheck = parts->takeFirst(); FeaturePart* otherPart = _findConnectedPart( partCheck, parts ); if ( otherPart ) { // remove partCheck from r-tree double checkpartBMin[2], checkpartBMax[2]; partCheck->getBoundingBox( checkpartBMin, checkpartBMax ); double otherPartBMin[2], otherPartBMax[2]; otherPart->getBoundingBox( otherPartBMin, otherPartBMax ); // merge points from partCheck to p->item if ( otherPart->mergeWithFeaturePart( partCheck ) ) { // remove the parts we are joining from the index mFeatureIndex->Remove( checkpartBMin, checkpartBMax, partCheck ); mFeatureIndex->Remove( otherPartBMin, otherPartBMax, otherPart ); // reinsert merged line to r-tree (probably not needed) otherPart->getBoundingBox( otherPartBMin, otherPartBMax ); mFeatureIndex->Insert( otherPartBMin, otherPartBMax, otherPart ); mConnectedFeaturesIds.insert( partCheck->featureId(), connectedFeaturesId ); mConnectedFeaturesIds.insert( otherPart->featureId(), connectedFeaturesId ); mFeatureParts.removeOne( partCheck ); delete partCheck; } } } // we're done processing feature parts with this particular label text delete parts; mConnectedHashtable.remove( labelText ); } // we're done processing connected features //should be empty, but clear to be safe qDeleteAll( mConnectedHashtable ); mConnectedHashtable.clear(); mConnectedTexts.clear(); }
bool KNMusicPlaylistiTunesXMLParser::write(KNMusicPlaylistModel *playlist, const QString &filePath) { //Generate the plist document. QDomDocument plistDocument; //Initial the plist element. QDomElement plistRoot=plistDocument.createElement("plist"); plistRoot.setAttribute("version", "1.0"); plistDocument.appendChild(plistRoot); //Initial the dict element. QDomElement dictElement=plistDocument.createElement("dict"); plistRoot.appendChild(dictElement); appendDictValue(plistDocument, dictElement, "Major Version", 1); appendDictValue(plistDocument, dictElement, "Minor Version", 1); appendDictValue(plistDocument, dictElement, "Date", QDateTime::currentDateTime()); appendDictValue(plistDocument, dictElement, "Features", 5); appendDictValue(plistDocument, dictElement, "Show Content Ratings", true); //Generate database and song index list. QHash<QString, int> filePathIndex; QLinkedList<int> playlistIndexList; //Add paths to hash keys. for(int i=0; i<playlist->rowCount(); i++) { //Get current path. QString currentPath=playlist->rowProperty(i, FilePathRole).toString(); //Check the path in the index hash. int currentIndex=filePathIndex.value(currentPath, -1); //If we never insert this path to the index, if(currentIndex==-1) { //Get the new index. currentIndex=i; //Insert the path to the hash. filePathIndex.insert(currentPath, currentIndex); } //Append the list. playlistIndexList.append(currentIndex); } //Output the database info to dict. //Initial the elements. QDomElement tracksKey=plistDocument.createElement("key"), tracksDict=plistDocument.createElement("dict"); QDomText tracksKeyValue=plistDocument.createTextNode("Tracks"); tracksKey.appendChild(tracksKeyValue); //Add to dict elements. dictElement.appendChild(tracksKey); dictElement.appendChild(tracksDict); //Write database info. QList<int> songIndexList=filePathIndex.values(); while(!songIndexList.isEmpty()) { int currentRow=songIndexList.takeFirst(), trackID=currentRow+100; //Generate current row key and dict. QDomElement trackKey=plistDocument.createElement("key"), trackDict=plistDocument.createElement("dict"); //Generate the track key value. QDomText trackKeyValue= plistDocument.createTextNode(QString::number(trackID)); trackKey.appendChild(trackKeyValue); //Get the detail info. const KNMusicDetailInfo detailInfo=playlist->rowDetailInfo(currentRow); //Generate the dict. appendDictValue(plistDocument, trackDict, "Track ID", trackID); appendDictValue(plistDocument, trackDict, "Name", detailInfo.textLists[Name]); appendDictValue(plistDocument, trackDict, "Artist", detailInfo.textLists[Artist]); appendDictValue(plistDocument, trackDict, "Album Artist", detailInfo.textLists[AlbumArtist]); appendDictValue(plistDocument, trackDict, "Genre", detailInfo.textLists[Genre]); appendDictValue(plistDocument, trackDict, "Kind", detailInfo.textLists[Kind]); appendDictValue(plistDocument, trackDict, "Size", detailInfo.size); appendDictValue(plistDocument, trackDict, "Total Time", detailInfo.duration); appendDictValue(plistDocument, trackDict, "Track Number", detailInfo.textLists[TrackNumber].toString().toInt()); appendDictValue(plistDocument, trackDict, "Track Count", detailInfo.textLists[TrackCount].toString().toInt()); appendDictValue(plistDocument, trackDict, "Year", detailInfo.textLists[Year].toString().toInt()); appendDictValue(plistDocument, trackDict, "Date Modified", detailInfo.dateModified); appendDictValue(plistDocument, trackDict, "Date Added", detailInfo.dateAdded); appendDictValue(plistDocument, trackDict, "Bit Rate", detailInfo.bitRate); appendDictValue(plistDocument, trackDict, "Sample Rate", detailInfo.samplingRate); appendDictValue(plistDocument, trackDict, "Track Type", "File"); QString fileLocate=QUrl::fromLocalFile(detailInfo.filePath).toString(); #ifdef Q_OS_WIN fileLocate.insert(7, "localhost"); #endif appendDictValue(plistDocument, trackDict, "Location", QString(QUrl::toPercentEncoding(fileLocate, "/:"))); appendDictValue(plistDocument, trackDict, "File Folder Count", -1); appendDictValue(plistDocument, trackDict, "Library Folder Count", -1); //Add the key and dict to track. tracksDict.appendChild(trackKey); tracksDict.appendChild(trackDict); } //Output the playlist info to dict. QDomElement playlistKey=plistDocument.createElement("key"), playlistArray=plistDocument.createElement("array"); QDomText playlistKeyValue=plistDocument.createTextNode("Playlists"); playlistKey.appendChild(playlistKeyValue); //Add the key and array to dict element. dictElement.appendChild(playlistKey); dictElement.appendChild(playlistArray); //Generate current playlist information. QDomElement playlistDict=plistDocument.createElement("dict"); playlistArray.appendChild(playlistDict); //Set playlist information to the dict. appendDictValue(plistDocument, playlistDict, "Name", playlist->title()); appendDictValue(plistDocument, playlistDict, "Playlist ID", "99"); appendDictValue(plistDocument, playlistDict, "All Items", true); //Generate playlist items array. QDomElement playlistItemsKey=plistDocument.createElement("key"), playlistItemsArray=plistDocument.createElement("array"); QDomText playlistItemsKeyValue= plistDocument.createTextNode("Playlist Items"); playlistItemsKey.appendChild(playlistItemsKeyValue); //Add to current playlist dict. playlistDict.appendChild(playlistItemsKey); playlistDict.appendChild(playlistItemsArray); //Generate Track ID dicts. while(!playlistIndexList.isEmpty()) { QDomElement currentTrackDict=plistDocument.createElement("dict"); appendDictValue(plistDocument, currentTrackDict, "Track ID", 100+playlistIndexList.takeFirst()); //Add the dict to the array. playlistItemsArray.appendChild(currentTrackDict); } return writeContent(filePath, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE " "plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"" " \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + plistDocument.toString(4)); }
bool Layer::registerFeature( const QString& geom_id, PalGeometry *userGeom, double label_x, double label_y, const QString &labelText, double labelPosX, double labelPosY, bool fixedPos, double angle, bool fixedAngle, int xQuadOffset, int yQuadOffset, double xOffset, double yOffset, bool alwaysShow, double repeatDistance ) { if ( geom_id.isEmpty() || label_x < 0 || label_y < 0 ) return false; mMutex.lock(); if ( hashtable->contains( geom_id ) ) { mMutex.unlock(); //A feature with this id already exists. Don't throw an exception as sometimes, //the same feature is added twice (dateline split with otf-reprojection) return false; } // Split MULTI GEOM and Collection in simple geometries const GEOSGeometry *the_geom = userGeom->getGeosGeometry(); Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y ); if ( fixedPos ) { f->setFixedPosition( labelPosX, labelPosY ); } if ( xQuadOffset != 0 || yQuadOffset != 0 ) { f->setQuadOffset( xQuadOffset, yQuadOffset ); } if ( xOffset != 0.0 || yOffset != 0.0 ) { f->setPosOffset( xOffset, yOffset ); } if ( fixedAngle ) { f->setFixedAngle( angle ); } // use layer-level defined rotation, but not if position fixed if ( !fixedPos && angle != 0.0 ) { f->setFixedAngle( angle ); } f->setRepeatDistance( repeatDistance ); f->setAlwaysShow( alwaysShow ); bool first_feat = true; double geom_size = -1, biggest_size = -1; FeaturePart* biggest_part = NULL; // break the (possibly multi-part) geometry into simple geometries QLinkedList<const GEOSGeometry*>* simpleGeometries = unmulti( the_geom ); if ( simpleGeometries == NULL ) // unmulti() failed? { mMutex.unlock(); throw InternalException::UnknownGeometry(); } GEOSContextHandle_t geosctxt = geosContext(); while ( simpleGeometries->size() > 0 ) { const GEOSGeometry* geom = simpleGeometries->takeFirst(); // ignore invalid geometries (e.g. polygons with self-intersecting rings) if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception { // std::cerr << "ignoring invalid feature " << geom_id << std::endl; continue; } int type = GEOSGeomTypeId_r( geosctxt, geom ); if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON ) { mMutex.unlock(); throw InternalException::UnknownGeometry(); } FeaturePart* fpart = new FeaturePart( f, geom ); // ignore invalid geometries if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) || ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) ) { delete fpart; continue; } // polygons: reorder coordinates if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 ) { delete fpart; continue; } if ( mMode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) ) { if ( type == GEOS_LINESTRING ) GEOSLength_r( geosctxt, geom, &geom_size ); else if ( type == GEOS_POLYGON ) GEOSArea_r( geosctxt, geom, &geom_size ); if ( geom_size > biggest_size ) { biggest_size = geom_size; delete biggest_part; // safe with NULL part biggest_part = fpart; } continue; // don't add the feature part now, do it later // TODO: we should probably add also other parts to act just as obstacles } // feature part is ready! addFeaturePart( fpart, labelText ); first_feat = false; } delete simpleGeometries; userGeom->releaseGeosGeometry( the_geom ); mMutex.unlock(); // if using only biggest parts... if (( mMode == LabelPerFeature || f->fixedPosition() ) && biggest_part != NULL ) { addFeaturePart( biggest_part, labelText ); first_feat = false; } // add feature to layer if we have added something if ( !first_feat ) { features->append( f ); hashtable->insert( geom_id, f ); } else { delete f; } return !first_feat; // true if we've added something }
void PointSet::splitPolygons( QLinkedList<PointSet *> &shapes_toProcess, QLinkedList<PointSet *> &shapes_final, double xrm, double yrm ) { int i, j; int nbp; double *x = nullptr; double *y = nullptr; int *pts = nullptr; int *cHull = nullptr; int cHullSize; double cp; double bestcp = 0; double bestArea = 0; double area; double base; double b, c; double s; int ihs; int ihn; int ips; int ipn; int holeS = -1; // hole start and end points int holeE = -1; int retainedPt = -1; int pt = 0; double labelArea = xrm * yrm; PointSet *shape = nullptr; while ( !shapes_toProcess.isEmpty() ) { shape = shapes_toProcess.takeFirst(); x = shape->x; y = shape->y; nbp = shape->nbPoints; pts = new int[nbp]; for ( i = 0; i < nbp; i++ ) { pts[i] = i; } // conpute convex hull shape->cHullSize = GeomFunction::convexHullId( pts, x, y, nbp, shape->cHull ); cHull = shape->cHull; cHullSize = shape->cHullSize; bestArea = 0; retainedPt = -1; // lookup for a hole for ( ihs = 0; ihs < cHullSize; ihs++ ) { // ihs->ihn => cHull'seg ihn = ( ihs + 1 ) % cHullSize; ips = cHull[ihs]; ipn = ( ips + 1 ) % nbp; if ( ipn != cHull[ihn] ) // next point on shape is not the next point on cHull => there is a hole here ! { bestcp = 0; pt = -1; // lookup for the deepest point in the hole for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp ) { cp = std::fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], x[cHull[ihn]], y[cHull[ihn]], x[i], y[i] ) ); if ( cp - bestcp > EPSILON ) { bestcp = cp; pt = i; } } if ( pt != -1 ) { // compute the ihs->ihn->pt triangle's area base = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]], x[cHull[ihn]], y[cHull[ihn]] ); b = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]], x[pt], y[pt] ); c = GeomFunction::dist_euc2d( x[cHull[ihn]], y[cHull[ihn]], x[pt], y[pt] ); s = ( base + b + c ) / 2; // s = half perimeter area = s * ( s - base ) * ( s - b ) * ( s - c ); if ( area < 0 ) area = -area; // retain the biggest area if ( area - bestArea > EPSILON ) { bestArea = area; retainedPt = pt; holeS = ihs; holeE = ihn; } } } } // we have a hole, its area, and the deppest point in hole // we're going to find the second point to cup the shape // holeS = hole starting point // holeE = hole ending point // retainedPt = deppest point in hole // bestArea = area of triangle HoleS->holeE->retainedPoint bestArea = std::sqrt( bestArea ); double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0; int ps = -1, pe = -1, fps = -1, fpe = -1; if ( retainedPt >= 0 && bestArea > labelArea ) // there is a hole so we'll cut the shape in two new shape (only if hole area is bigger than twice labelArea) { c = std::numeric_limits<double>::max(); // iterate on all shape points except points which are in the hole bool isValid; int k, l; for ( i = ( cHull[holeE] + 1 ) % nbp; i != ( cHull[holeS] - 1 + nbp ) % nbp; i = j ) { j = ( i + 1 ) % nbp; // i->j is shape segment not in hole // compute distance between retainedPoint and segment // whether perpendicular distance (if retaindPoint is fronting segment i->j) // or distance between retainedPt and i or j (choose the nearest) seg_length = GeomFunction::dist_euc2d( x[i], y[i], x[j], y[j] ); cx = ( x[i] + x[j] ) / 2.0; cy = ( y[i] + y[j] ) / 2.0; dx = cy - y[i]; dy = cx - x[i]; ex = cx - dx; ey = cy + dy; fx = cx + dx; fy = cy - dy; if ( seg_length < EPSILON || std::fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j { if ( ( ex = GeomFunction::dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = GeomFunction::dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) ) { b = ex; ps = i; pe = i; } else { b = ey; ps = j; pe = j; } } else // point fronting i->j => compute pependicular distance => create a new point { b = GeomFunction::cross_product( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt] ) / seg_length; b *= b; ps = i; pe = j; if ( !GeomFunction::computeLineIntersection( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt], x[retainedPt] - dx, y[retainedPt] + dy, &ptx, &pty ) ) { //error - it should intersect the line } } isValid = true; double pointX, pointY; if ( ps == pe ) { pointX = x[pe]; pointY = y[pe]; } else { pointX = ptx; pointY = pty; } for ( k = cHull[holeS]; k != cHull[holeE]; k = ( k + 1 ) % nbp ) { l = ( k + 1 ) % nbp; if ( GeomFunction::isSegIntersects( x[retainedPt], y[retainedPt], pointX, pointY, x[k], y[k], x[l], y[l] ) ) { isValid = false; break; } } if ( isValid && b < c ) { c = b; fps = ps; fpe = pe; fptx = ptx; fpty = pty; } } // for point which are not in hole // we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint] int imin = retainedPt; int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? std::min( fps, fpe ) : std::max( fps, fpe ) ); int nbPtSh1, nbPtSh2; // how many points in new shapes ? if ( imax > imin ) nbPtSh1 = imax - imin + 1 + ( fpe != fps ); else nbPtSh1 = imax + nbp - imin + 1 + ( fpe != fps ); if ( ( imax == fps ? fpe : fps ) < imin ) nbPtSh2 = imin - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps ); else nbPtSh2 = imin + nbp - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps ); if ( retainedPt == -1 || fps == -1 || fpe == -1 ) { if ( shape->parent ) delete shape; } // check for useless spliting else if ( imax == imin || nbPtSh1 <= 2 || nbPtSh2 <= 2 || nbPtSh1 == nbp || nbPtSh2 == nbp ) { shapes_final.append( shape ); } else { PointSet *newShape = shape->extractShape( nbPtSh1, imin, imax, fps, fpe, fptx, fpty ); if ( shape->parent ) newShape->parent = shape->parent; else newShape->parent = shape; shapes_toProcess.append( newShape ); if ( imax == fps ) imax = fpe; else imax = fps; newShape = shape->extractShape( nbPtSh2, imax, imin, fps, fpe, fptx, fpty ); if ( shape->parent ) newShape->parent = shape->parent; else newShape->parent = shape; shapes_toProcess.append( newShape ); if ( shape->parent ) delete shape; } } else { shapes_final.append( shape ); } delete[] pts; } }
bool KNMusicLRCLyricsParser::parseData(const QString &lyricsTextData, QList<qint64> &positionList, QStringList &textList) { //Clear the position list and text list. positionList.clear(); textList.clear(); //Split the lyrics text data. QStringList lyricsRawData=lyricsTextData.split(QRegExp("\n"), QString::SkipEmptyParts); QList<LyricsLine> lyricsLineList; //Remove the same line in the lyrics raw data. lyricsRawData.removeDuplicates(); //Parse the lyrics raw data. while(!lyricsRawData.isEmpty()) { //Get the first line of the current list and remove all spaces. QString currentLine=lyricsRawData.takeFirst().simplified(); //Find frames in the current line. QRegularExpressionMatchIterator frameMatcher= m_frameCatchRegExp.globalMatch(currentLine); int lastPosition=0; QLinkedList<QString> frameLists; //Get all the frames. while(frameMatcher.hasNext()) { QRegularExpressionMatch matchedFrame=frameMatcher.next(); //Check is the current matched frame is at the last position. //An example is: // [00:00:01] Won't chu kiss me![00:00:03]Saikou no if(matchedFrame.capturedStart()!=lastPosition) { //Then we should pick out the data before the current start and //last position. //Like 'Won't chu kiss me!' in the example. QString text=currentLine.mid(lastPosition, matchedFrame.capturedStart()-lastPosition); //Parse the datas to frame lists. while(!frameLists.isEmpty()) { parseFrames(frameLists.takeFirst(), text, lyricsLineList); } } //Add current frame to frame list. frameLists.append(currentLine.mid(matchedFrame.capturedStart(), matchedFrame.capturedLength())); //Update the last position. lastPosition=matchedFrame.capturedEnd(); } //Remove the previous datas, and parse the left datas to frame lists. currentLine.remove(0, lastPosition); while(!frameLists.isEmpty()) { parseFrames(frameLists.takeFirst(), currentLine, lyricsLineList); } } //Check is the lyrics line list is empty or not, if it's empty, means we //can't parse it. if(lyricsLineList.isEmpty()) { return false; } //Sorr the lyrics line. //- Why stable sort? // Because there might be some frames at the same time. Display them with //their exist order. qStableSort(lyricsLineList.begin(), lyricsLineList.end(), frameLessThan); //Combine the same timestamp lyrics. QMap<qint64, QString> lyricsCombineMap; for(QList<LyricsLine>::iterator i=lyricsLineList.begin(); i!=lyricsLineList.end(); ++i) { lyricsCombineMap.insert((*i).position, lyricsCombineMap.contains((*i).position)? lyricsCombineMap.value((*i).position)+'\n'+(*i).text: (*i).text); } //Export the position and the text. positionList=lyricsCombineMap.keys(); textList=lyricsCombineMap.values(); return true; }