ErrorList topolTest::checkValid( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer1 ); Q_UNUSED( layer2 ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; QgsFeature f; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( ++i ); if ( testCancelled() ) break; QgsGeometry* g = it->feature.geometry(); if ( !g ) { QgsMessageLog::logMessage( tr( "Invalid geometry in validity test." ), tr( "Topology plugin" ) ); continue; } if ( !g->asGeos() ) continue; if ( !GEOSisValid_r( QgsGeometry::getGEOSHandler(), g->asGeos() ) ) { QgsRectangle r = g->boundingBox(); QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry* conflict = new QgsGeometry( *g ); TopolErrorValid* err = new TopolErrorValid( r, conflict, fls ); errorList << err; } } return errorList; }
ErrorList topolTest::checkMultipart( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer2 ); Q_UNUSED( layer1 ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( ++i ); if ( testCancelled() ) break; QgsGeometry* g = it->feature.geometry(); if ( !g ) { QgsMessageLog::logMessage( tr( "Missing geometry in multipart check." ), tr( "Topology plugin" ) ); continue; } if ( !g->asGeos() ) continue; if ( g->isMultipart() ) { QgsRectangle r = g->boundingBox(); QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry* conflict = new QgsGeometry( *g ); TopolErroMultiPart* err = new TopolErroMultiPart( r, conflict, fls ); errorList << err; } } return errorList; }
QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, QgsRenderContext &context, QgsGeometry* obstacleGeometry ) { const QgsMapSettings& mapSettings = mEngine->mapSettings(); const QgsDiagramRenderer* dr = mSettings.getRenderer(); if ( dr ) { QList<QgsDiagramSettings> settingList = dr->diagramSettings(); if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility ) { double minScale = settingList.at( 0 ).minScaleDenominator; if ( minScale > 0 && context.rendererScale() < minScale ) { return nullptr; } double maxScale = settingList.at( 0 ).maxScaleDenominator; if ( maxScale > 0 && context.rendererScale() > maxScale ) { return nullptr; } } } //convert geom to geos QgsGeometry geom = feat.geometry(); QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() ); if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) ) { //PAL features are prerotated, so extent also needs to be unrotated extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() ); } const GEOSGeometry* geos_geom = nullptr; QScopedPointer<QgsGeometry> scopedPreparedGeom; if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) { scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) ); QgsGeometry* preparedGeom = scopedPreparedGeom.data(); if ( preparedGeom->isEmpty() ) return nullptr; geos_geom = preparedGeom->asGeos(); } else { geos_geom = geom.asGeos(); } if ( !geos_geom ) return nullptr; // invalid geometry GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ); const GEOSGeometry* geosObstacleGeom = nullptr; QScopedPointer<QgsGeometry> scopedObstacleGeom; if ( mSettings.isObstacle() && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ) ) { QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ); geosObstacleGeom = preparedObstacleGeom.asGeos(); } else if ( mSettings.isObstacle() && obstacleGeometry ) { geosObstacleGeom = obstacleGeometry->asGeos(); } GEOSGeometry* geosObstacleGeomClone = nullptr; if ( geosObstacleGeom ) { geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom ); } double diagramWidth = 0; double diagramHeight = 0; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat, context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } } // feature to the layer bool alwaysShow = mSettings.showAllDiagrams(); int ddColX = mSettings.xPosColumn; int ddColY = mSettings.yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos && ! feat.attribute( ddColX ).isNull() && ! feat.attribute( ddColY ).isNull() ) { bool posXOk, posYOk; ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ); ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ); if ( !posXOk || !posYOk ) { ddPos = false; } else { QgsCoordinateTransform ct = mSettings.coordinateTransform(); if ( ct.isValid() && !ct.isShortCircuited() ) { double z = 0; ct.transformInPlace( ddPosX, ddPosY, z ); } //data defined diagram position is always centered ddPosX -= diagramWidth / 2.0; ddPosY -= diagramHeight / 2.0; } } else ddPos = false; int ddColShow = mSettings.showColumn; if ( ddColShow >= 0 && ! feat.attribute( ddColShow ).isNull() ) { bool showOk; bool ddShow = feat.attribute( ddColShow ).toDouble( &showOk ); if ( showOk && ! ddShow ) return nullptr; } QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) ); lf->setHasFixedPosition( ddPos ); lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) ); lf->setHasFixedAngle( true ); lf->setFixedAngle( 0 ); lf->setAlwaysShow( alwaysShow ); lf->setIsObstacle( mSettings.isObstacle() ); lf->setZIndex( mSettings.getZIndex() ); if ( geosObstacleGeomClone ) { lf->setObstacleGeometry( geosObstacleGeomClone ); } if ( dr ) { //append the diagram attributes to lbl lf->setAttributes( feat.attributes() ); } QgsPoint ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 ); QgsPoint ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 ); lf->setDistLabel( ptOne.distance( ptZero ) * mSettings.distance() ); return lf; }
void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context ) { //get diagram layer settings, diagram renderer QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layer ); if ( layerIt == mActiveDiagramLayers.constEnd() ) { return; } //convert geom to geos QgsGeometry* geom = feat.geometry(); if ( layerIt.value().ct && !willUseLayer( layer ) ) // reproject the geometry if feature not already transformed for labeling { geom->transform( *( layerIt.value().ct ) ); } GEOSGeometry* geos_geom = geom->asGeos(); if ( geos_geom == 0 ) { return; // invalid geometry } //create PALGeometry with diagram = true QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone( geos_geom ) ); lbl->setIsDiagram( true ); // record the created geometry - it will be deleted at the end. layerIt.value().geometries.append( lbl ); double diagramWidth = 0; double diagramHeight = 0; QgsDiagramRendererV2* dr = layerIt.value().renderer; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat.attributeMap(), context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } //append the diagram attributes to lbl QList<int> diagramAttrib = dr->diagramAttributes(); QList<int>::const_iterator diagAttIt = diagramAttrib.constBegin(); for ( ; diagAttIt != diagramAttrib.constEnd(); ++diagAttIt ) { lbl->addDiagramAttribute( *diagAttIt, feat.attributeMap()[*diagAttIt] ); } } // register feature to the layer int ddColX = layerIt.value().xPosColumn; int ddColY = layerIt.value().yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos ) { bool posXOk, posYOk; //data defined diagram position is always centered ddPosX = feat.attributeMap()[ddColX].toDouble( &posXOk ) - diagramWidth / 2.0; ddPosY = feat.attributeMap()[ddColY].toDouble( &posYOk ) - diagramHeight / 2.0; if ( !posXOk || !posYOk ) { ddPos = false; } else { const QgsCoordinateTransform* ct = layerIt.value().ct; if ( ct ) { double z = 0; ct->transformInPlace( ddPosX, ddPosY, z ); } } } try { if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) ) { return; } } catch ( std::exception &e ) { Q_UNUSED( e ); QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( feat.id() ) + QString::fromLatin1( e.what() ) ); return; } pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() ); QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 ); QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 ); palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist ); }
void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context ) { QString labelText; if ( formatNumbers == true && ( f.attributeMap()[fieldIndex].type() == QVariant::Int || f.attributeMap()[fieldIndex].type() == QVariant::Double ) ) { QString numberFormat; double d = f.attributeMap()[fieldIndex].toDouble(); if ( d > 0 && plusSign == true ) { numberFormat.append( "+" ); } numberFormat.append( "%1" ); labelText = numberFormat.arg( d, 0, 'f', decimals ); } else { labelText = f.attributeMap()[fieldIndex].toString(); } double labelX, labelY; // will receive label size QFont labelFont = textFont; //data defined label size? QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size ); if ( it != dataDefinedProperties.constEnd() ) { //find out size QVariant size = f.attributeMap().value( *it ); if ( size.isValid() ) { double sizeDouble = size.toDouble(); if ( sizeDouble <= 0 ) { return; } labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) ); } QFontMetricsF labelFontMetrics( labelFont ); calculateLabelSize( &labelFontMetrics, labelText, labelX, labelY ); } else { calculateLabelSize( fontMetrics, labelText, labelX, labelY ); } QgsGeometry* geom = f.geometry(); if ( ct ) // reproject the geometry if necessary geom->transform( *ct ); if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) ) { return; } // CLIP the geometry if it is bigger than the extent QgsGeometry* geomClipped = NULL; GEOSGeometry* geos_geom; bool do_clip = !extentGeom->contains( geom ); if ( do_clip ) { geomClipped = geom->intersection( extentGeom ); // creates new geometry geos_geom = geomClipped->asGeos(); } else { geos_geom = geom->asGeos(); } if ( geos_geom == NULL ) return; // invalid geometry GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom ); if ( do_clip ) delete geomClipped; //data defined position / alignment / rotation? bool dataDefinedPosition = false; bool dataDefinedRotation = false; double xPos = 0.0, yPos = 0.0, angle = 0.0; bool ddXPos, ddYPos; QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX ); if ( dPosXIt != dataDefinedProperties.constEnd() ) { QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY ); if ( dPosYIt != dataDefinedProperties.constEnd() ) { //data defined position. But field values could be NULL -> positions will be generated by PAL xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos ); yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos ); if ( ddXPos && ddYPos ) { dataDefinedPosition = true; //x/y shift in case of alignment double xdiff = 0; double ydiff = 0; //horizontal alignment QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali ); if ( haliIt != dataDefinedProperties.end() ) { QString haliString = f.attributeMap().value( *haliIt ).toString(); if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX / 2.0; } else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX; } } //vertical alignment QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali ); if ( valiIt != dataDefinedProperties.constEnd() ) { QString valiString = f.attributeMap().value( *valiIt ).toString(); if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 ) { if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY; } else { QFontMetrics labelFontMetrics( labelFont ); double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height(); if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; } else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; ydiff -= labelY * 0.5 * ( 1 - descentRatio ); } } } } //data defined rotation? QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation ); if ( rotIt != dataDefinedProperties.constEnd() ) { dataDefinedRotation = true; angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180; //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center double xd = xdiff * cos( angle ) - ydiff * sin( angle ); double yd = xdiff * sin( angle ) + ydiff * cos( angle ); xdiff = xd; ydiff = yd; } //project xPos and yPos from layer to map CRS double z = 0; if ( ct ) { ct->transformInPlace( xPos, yPos, z ); } yPos += ydiff; xPos += xdiff; } } } QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, geos_geom_clone ); // record the created geometry - it will be deleted at the end. geometries.append( lbl ); // register feature to the layer try { if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(), xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation ) ) return; } catch ( std::exception &e ) { Q_UNUSED( e ); QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( f.id() ) + QString::fromLatin1( e.what() ) ); return; } // TODO: only for placement which needs character info pal::Feature* feat = palLayer->getFeature( lbl->strId() ); feat->setLabelInfo( lbl->info( fontMetrics, xform, rasterCompressFactor ) ); // TODO: allow layer-wide feature dist in PAL...? //data defined label-feature distance? double distance = dist; QMap< DataDefinedProperties, int >::const_iterator dDistIt = dataDefinedProperties.find( QgsPalLayerSettings::LabelDistance ); if ( dDistIt != dataDefinedProperties.constEnd() ) { distance = f.attributeMap().value( *dDistIt ).toDouble(); } if ( distance != 0 ) { if ( distInMapUnits ) //convert distance from mm/map units to pixels { distance /= context.mapToPixel().mapUnitsPerPixel(); } else //mm { distance *= vectorScaleFactor; } feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance ); } //add parameters for data defined labeling to QgsPalGeometry QMap< DataDefinedProperties, int >::const_iterator dIt = dataDefinedProperties.constBegin(); for ( ; dIt != dataDefinedProperties.constEnd(); ++dIt ) { lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] ); } }
ErrorList topolTest::checkGaps( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); // could be enabled for lines and points too // so duplicate rule may be removed? if ( layer1->geometryType() != QGis::Polygon ) { return errorList; } QList<FeatureLayer>::Iterator it; QgsGeometry* g1; QList<GEOSGeometry*> geomList; qDebug() << mFeatureList1.count() << " features in list!"; QList<FeatureLayer>::ConstIterator FeatureListEnd = mFeatureList1.end(); for ( it = mFeatureList1.begin(); it != FeatureListEnd; ++it ) { qDebug() << "reading features-" << i; if ( !( ++i % 100 ) ) { emit progress( i ); } if ( testCancelled() ) { break; } g1 = it->feature.geometry(); if ( !g1 ) { continue; } if ( !g1->asGeos() ) { continue; } if ( !g1->isGeosValid() ) { qDebug() << "invalid geometry found..skipping.." << it->feature.id(); continue; } if ( g1->isMultipart() ) { QgsMultiPolygon polys = g1->asMultiPolygon(); for ( int m = 0; m < polys.count(); m++ ) { QgsPolygon polygon = polys[m]; QgsGeometry* polyGeom = QgsGeometry::fromPolygon( polygon ); geomList.push_back( GEOSGeom_clone_r( geosctxt, polyGeom->asGeos() ) ); delete polyGeom; } } else { geomList.push_back( GEOSGeom_clone_r( geosctxt, g1->asGeos() ) ); } } GEOSGeometry** geomArray = new GEOSGeometry*[geomList.size()]; for ( int i = 0; i < geomList.size(); ++i ) { //qDebug() << "filling geometry array-" << i; geomArray[i] = geomList.at( i ); } qDebug() << "creating geometry collection-"; if ( geomList.size() == 0 ) { //qDebug() << "geometry list is empty!"; delete [] geomArray; return errorList; } GEOSGeometry* collection = 0; collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomArray, geomList.size() ); qDebug() << "performing cascaded union..might take time..-"; GEOSGeometry* unionGeom = GEOSUnionCascaded_r( geosctxt, collection ); //delete[] geomArray; QgsGeometry test; test.fromGeos( unionGeom ); //qDebug() << "wktmerged - " << test.exportToWkt(); QString extentWkt = test.boundingBox().asWktPolygon(); QgsGeometry* extentGeom = QgsGeometry::fromWkt( extentWkt ); QgsGeometry* bufferExtent = extentGeom->buffer( 2, 3 ); delete extentGeom; //qDebug() << "extent wkt - " << bufferExtent->exportToWkt(); QgsGeometry* diffGeoms = bufferExtent->difference( &test ); delete bufferExtent; if ( !diffGeoms ) { qDebug() << "difference result 0-"; return errorList; } //qDebug() << "difference gometry - " << diffGeoms->exportToWkt(); QList<QgsGeometry*> geomColl = diffGeoms->asGeometryCollection(); QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( int i = 1; i < geomColl.count() ; ++i ) { QgsGeometry* conflictGeom = geomColl[i]; if ( isExtent ) { if ( canvasExtentPoly->disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly->crosses( conflictGeom ) ) { conflictGeom = conflictGeom->intersection( canvasExtentPoly ); } } QgsRectangle bBox = conflictGeom->boundingBox(); QgsFeature feat; FeatureLayer ftrLayer1; ftrLayer1.layer = layer1; QList<FeatureLayer> errorFtrLayers; errorFtrLayers << ftrLayer1 << ftrLayer1; TopolErrorGaps* err = new TopolErrorGaps( bBox, conflictGeom, errorFtrLayers ); errorList << err; } delete canvasExtentPoly; return errorList; }