QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, QgsRenderContext &context, QgsGeometry* obstacleGeometry ) { const QgsMapSettings& mapSettings = mEngine->mapSettings(); QgsDiagramRendererV2* dr = mSettings.renderer; 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 0; } double maxScale = settingList.at( 0 ).maxScaleDenominator; if ( maxScale > 0 && context.rendererScale() > maxScale ) { return 0; } } } //convert geom to geos const QgsGeometry* geom = feat.constGeometry(); QScopedPointer<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 = 0; QScopedPointer<QgsGeometry> preparedGeom; if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.ct, extentGeom.data() ) ) { preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, mSettings.ct, extentGeom.data() ) ); if ( !preparedGeom.data() ) return 0; geos_geom = preparedGeom.data()->asGeos(); } else { geos_geom = geom->asGeos(); } if ( geos_geom == 0 ) { return 0; // invalid geometry } GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ); const GEOSGeometry* geosObstacleGeom = 0; QScopedPointer<QgsGeometry> scopedObstacleGeom; if ( mSettings.obstacle && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, mSettings.ct, extentGeom.data() ) ) { scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, mSettings.ct, extentGeom.data() ) ); geosObstacleGeom = scopedObstacleGeom.data()->asGeos(); } else if ( mSettings.obstacle && obstacleGeometry ) { geosObstacleGeom = obstacleGeometry->asGeos(); } GEOSGeometry* geosObstacleGeomClone = 0; 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.showAll; int ddColX = mSettings.xPosColumn; int ddColY = mSettings.yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos ) { bool posXOk, posYOk; ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ); ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ); if ( !posXOk || !posYOk ) { ddPos = false; } else { const QgsCoordinateTransform* ct = mSettings.ct; if ( ct ) { double z = 0; ct->transformInPlace( ddPosX, ddPosY, z ); } //data defined diagram position is always centered ddPosX -= diagramWidth / 2.0; ddPosY -= diagramHeight / 2.0; } } 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.obstacle ); if ( geosObstacleGeomClone ) { lf->setObstacleGeometry( geosObstacleGeomClone ); } if ( dr ) { //append the diagram attributes to lbl lf->setAttributes( feat.attributes() ); } QgsPoint ptZero = mSettings.xform->toMapCoordinates( 0, 0 ); QgsPoint ptOne = mSettings.xform->toMapCoordinates( 1, 0 ); lf->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * mSettings.dist ); return lf; }
bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, QgsPoint point ) { if ( !layer ) return false; if ( layer->hasScaleBasedVisibility() && ( layer->minimumScale() > mCanvas->mapRenderer()->scale() || layer->maximumScale() <= mCanvas->mapRenderer()->scale() ) ) { QgsDebugMsg( "Out of scale limits" ); return false; } QMap< QString, QString > derivedAttributes; derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); // load identify radius from settings QSettings settings; double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble(); if ( identifyValue <= 0.0 ) identifyValue = QGis::DEFAULT_IDENTIFY_RADIUS; int featureCount = 0; QgsFeatureList featureList; // toLayerCoordinates will throw an exception for an 'invalid' point. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. try { // create the search rectangle double searchRadius = mCanvas->extent().width() * ( identifyValue / 100.0 ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featureList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } QgsFeatureList::iterator f_it = featureList.begin(); bool filter = false; QgsFeatureRendererV2* renderer = layer->rendererV2(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale for scale dependent visibility (rule based) renderer->startRender( *( mCanvas->mapRenderer()->rendererContext() ), layer ); filter = renderer->capabilities() & QgsFeatureRendererV2::Filter; } for ( ; f_it != featureList.end(); ++f_it ) { QgsFeatureId fid = f_it->id(); if ( filter && !renderer->willRenderFeature( *f_it ) ) continue; featureCount++; derivedAttributes.unite( featureDerivedAttributes( &( *f_it ), layer ) ); derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), *f_it, derivedAttributes ) ); } if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { renderer->stopRender( *( mCanvas->mapRenderer()->rendererContext() ) ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); return featureCount > 0; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, QgsRectangle viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); int capabilities = dprovider->capabilities(); if ( !dprovider || !( capabilities & QgsRasterDataProvider::Identify ) ) { return false; } try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QMap<int, QVariant> values; QgsRasterDataProvider::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( "identify/format" ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRasterDataProvider::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRasterDataProvider::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRasterDataProvider::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRasterDataProvider::IdentifyFormatText; else return false; } // We can only use context (extent, width, heigh) if layer is not reprojected, // otherwise we don't know source resolution (size). if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapRenderer()->destinationCrs() ) { viewExtent = toLayerCoordinates( layer, viewExtent ); values = dprovider->identify( point, format ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // Width and height are calculated from not projected extent and we hope that // are similar to source width and height used to reproject layer for drawing. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); values = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); //QString type = tr( "Raster" ); QgsGeometry geometry; if ( format == QgsRasterDataProvider::IdentifyFormatValue ) { foreach ( int bandNo, values.keys() ) { double value = values.value( bandNo ).toDouble(); QString valueString; if ( dprovider->isNoDataValue( bandNo, value ) ) { valueString = tr( "no data" ); } else { valueString = QgsRasterBlock::printValue( value ); } attributes.insert( dprovider->generateBandName( bandNo ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); }
void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, const QFont& f, const QColor& c, const QgsMapToPixel* xform, double bufferSize, const QColor& bufferColor, bool drawBuffer ) { QgsPoint outPt = xform->transform( label->getX(), label->getY() ); // TODO: optimize access :) const QgsPalLayerSettings& lyr = layer( label->getLayerName() ); QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text(); QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) ); //add the direction symbol if needed if ( !txt.isEmpty() && lyr.placement == QgsPalLayerSettings::Line && lyr.addDirectionSymbol && !lyr.multiLineLabels ) { if ( label->getReversed() ) { txt.prepend( "<" ); } else { txt.append( ">" ); } } //QgsDebugMsg( "drawLabel " + QString::number( drawBuffer ) + " " + txt ); QStringList multiLineList; if ( lyr.multiLineLabels ) { multiLineList = txt.split( "\n" ); } else { multiLineList << txt; } for ( int i = 0; i < multiLineList.size(); ++i ) { painter->save(); painter->translate( QPointF( outPt.x(), outPt.y() ) ); painter->rotate( -label->getAlpha() * 180 / M_PI ); // scale down painter: the font size has been multiplied by raster scale factor // to workaround a Qt font scaling bug with small font sizes painter->scale( 1.0 / lyr.rasterCompressFactor, 1.0 / lyr.rasterCompressFactor ); double yMultiLineOffset = ( multiLineList.size() - 1 - i ) * lyr.fontMetrics->height(); painter->translate( QPointF( 0, - lyr.fontMetrics->descent() - yMultiLineOffset ) ); if ( drawBuffer ) { // we're drawing buffer drawLabelBuffer( painter, multiLineList.at( i ), f, bufferSize * lyr.vectorScaleFactor * lyr.rasterCompressFactor , bufferColor ); } else { // we're drawing real label QPainterPath path; path.addText( 0, 0, f, multiLineList.at( i ) ); painter->setPen( Qt::NoPen ); painter->setBrush( c ); painter->drawPath( path ); } painter->restore(); if ( label->getNextPart() ) drawLabel( label->getNextPart(), painter, f, c, xform, bufferSize, bufferColor, drawBuffer ); } }
void QgsPalLabeling::drawLabeling( QgsRenderContext& context ) { Q_ASSERT( mMapRenderer != NULL ); QPainter* painter = context.painter(); QgsRectangle extent = context.extent(); if ( mLabelSearchTree ) { mLabelSearchTree->clear(); } QTime t; t.start(); // do the labeling itself double scale = mMapRenderer->scale(); // scale denominator QgsRectangle r = extent; double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() }; std::list<LabelPosition*>* labels; pal::Problem* problem; try { problem = mPal->extractProblem( scale, bbox ); } catch ( std::exception& e ) { Q_UNUSED( e ); QgsDebugMsg( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ) ); //mActiveLayers.clear(); // clean up return; } const QgsMapToPixel* xform = mMapRenderer->coordinateTransform(); // draw rectangles with all candidates // this is done before actual solution of the problem // before number of candidates gets reduced mCandidates.clear(); if ( mShowingCandidates && problem ) { painter->setPen( QColor( 0, 0, 0, 64 ) ); painter->setBrush( Qt::NoBrush ); for ( int i = 0; i < problem->getNumFeatures(); i++ ) { for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ ) { pal::LabelPosition* lp = problem->getFeatureCandidate( i, j ); drawLabelCandidateRect( lp, painter, xform ); } } } // find the solution labels = mPal->solveProblem( problem, mShowingAllLabels ); QgsDebugMsg( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ) ); t.restart(); painter->setRenderHint( QPainter::Antialiasing ); // draw the labels std::list<LabelPosition*>::iterator it = labels->begin(); for ( ; it != labels->end(); ++it ) { QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() ); if ( !palGeometry ) { continue; } if ( palGeometry->isDiagram() ) { //render diagram QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin(); for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit ) { if ( dit.key() && dit.key()->id().append( "d" ) == ( *it )->getLayerName() ) { QgsPoint outPt = xform->transform(( *it )->getX(), ( *it )->getY() ); dit.value().renderer->renderDiagram( palGeometry->diagramAttributes(), context, QPointF( outPt.x(), outPt.y() ) ); } } //insert into label search tree to manipulate position interactively if ( mLabelSearchTree ) { //for diagrams, remove the additional 'd' at the end of the layer id QString layerId = ( *it )->getLayerName(); layerId.chop( 1 ); mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, true ); } continue; } const QgsPalLayerSettings& lyr = layer(( *it )->getLayerName() ); QFont fontForLabel = lyr.textFont; QColor fontColor = lyr.textColor; double bufferSize = lyr.bufferSize; QColor bufferColor = lyr.bufferColor; //apply data defined settings for the label //font size QVariant dataDefinedSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Size ); if ( dataDefinedSize.isValid() ) { fontForLabel.setPixelSize( lyr.sizeToPixel( dataDefinedSize.toDouble(), context ) ); } //font color QVariant dataDefinedColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Color ); if ( dataDefinedColor.isValid() ) { fontColor.setNamedColor( dataDefinedColor.toString() ); if ( !fontColor.isValid() ) { fontColor = lyr.textColor; } } //font bold QVariant dataDefinedBold = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Bold ); if ( dataDefinedBold.isValid() ) { fontForLabel.setBold(( bool )dataDefinedBold.toInt() ); } //font italic QVariant dataDefinedItalic = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Italic ); if ( dataDefinedItalic.isValid() ) { fontForLabel.setItalic(( bool ) dataDefinedItalic.toInt() ); } //font underline QVariant dataDefinedUnderline = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Underline ); if ( dataDefinedUnderline.isValid() ) { fontForLabel.setUnderline(( bool ) dataDefinedUnderline.toInt() ); } //font strikeout QVariant dataDefinedStrikeout = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Strikeout ); if ( dataDefinedStrikeout.isValid() ) { fontForLabel.setStrikeOut(( bool ) dataDefinedStrikeout.toInt() ); } //font family QVariant dataDefinedFontFamily = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::Family ); if ( dataDefinedFontFamily.isValid() ) { fontForLabel.setFamily( dataDefinedFontFamily.toString() ); } //buffer size QVariant dataDefinedBufferSize = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferSize ); if ( dataDefinedBufferSize.isValid() ) { bufferSize = dataDefinedBufferSize.toDouble(); } //buffer color QVariant dataDefinedBufferColor = palGeometry->dataDefinedValues().value( QgsPalLayerSettings::BufferColor ); if ( dataDefinedBufferColor.isValid() ) { bufferColor.setNamedColor( dataDefinedBufferColor.toString() ); if ( !bufferColor.isValid() ) { bufferColor = lyr.bufferColor; } } if ( lyr.bufferSize != 0 ) drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferSize, bufferColor, true ); drawLabel( *it, painter, fontForLabel, fontColor, xform ); if ( mLabelSearchTree ) { mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName() ); } } QgsDebugMsg( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ) ); delete problem; delete labels; // delete all allocated geometries for features QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit; for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit ) { QgsPalLayerSettings& lyr = lit.value(); for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git ) delete *git; lyr.geometries.clear(); } //delete all allocated geometries for diagrams QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin(); for ( ; dIt != mActiveDiagramLayers.end(); ++dIt ) { QgsDiagramLayerSettings& dls = dIt.value(); for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git ) { delete *git; } dls.geometries.clear(); } }
double QgsPoint::azimuth( const QgsPoint& other ) { double dx = other.x() - m_x; double dy = other.y() - m_y; return ( atan2( dx, dy ) * 180.0 / M_PI ); }
bool QgsPoint::compare( const QgsPoint &other, double epsilon ) const { return ( qgsDoubleNear( m_x, other.x(), epsilon ) && qgsDoubleNear( m_y, other.y(), epsilon ) ); }
void QgsMapToolZoom::canvasReleaseEvent( QMouseEvent * e ) { if ( e->button() != Qt::LeftButton ) return; if ( mDragging ) { mDragging = false; delete mRubberBand; mRubberBand = 0; // store the rectangle mZoomRect.setRight( e->pos().x() ); mZoomRect.setBottom( e->pos().y() ); const QgsMapToPixel* coordinateTransform = mCanvas->getCoordinateTransform(); // set the extent to the zoomBox QgsPoint ll = coordinateTransform->toMapCoordinates( mZoomRect.left(), mZoomRect.bottom() ); QgsPoint ur = coordinateTransform->toMapCoordinates( mZoomRect.right(), mZoomRect.top() ); QgsRectangle r; r.setXMinimum( ll.x() ); r.setYMinimum( ll.y() ); r.setXMaximum( ur.x() ); r.setYMaximum( ur.y() ); r.normalize(); // prevent zooming to an empty extent if ( r.width() == 0 || r.height() == 0 ) { return; } if ( mZoomOut ) { QgsPoint cer = r.center(); QgsRectangle extent = mCanvas->extent(); double sf; if ( mZoomRect.width() > mZoomRect.height() ) { sf = extent.width() / r.width(); } else { sf = extent.height() / r.height(); } r.expand( sf ); QgsDebugMsg( QString( "Extent scaled by %1 to %2" ).arg( sf ).arg( r.toString().toLocal8Bit().constData() ) ); QgsDebugMsg( QString( "Center of currentExtent after scaling is %1" ).arg( r.center().toString().toLocal8Bit().constData() ) ); } mCanvas->setExtent( r ); mCanvas->refresh(); } else // not dragging { // change to zoom in/out by the default multiple mCanvas->zoomWithCenter( e->x(), e->y(), !mZoomOut ); } }
void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { //check if we operate on a vector layer //todo: move this to a function in parent class to avoid duplication QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { deleteTempRubberBand(); //find out bounding box of mCaptureList if ( size() < 1 ) { stopCapturing(); return; } QgsPoint firstPoint = points().at( 0 ); QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() ); for ( int i = 1; i < size(); ++i ) { bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; int reshapeReturn; bool reshapeDone = false; vlayer->beginEditCommand( tr( "Reshape" ) ); while ( fit.nextFeature( f ) ) { //query geometry //call geometry->reshape(mCaptureList) //register changed geometry in vector layer QgsGeometry geom = f.geometry(); if ( !geom.isEmpty() ) { reshapeReturn = geom.reshapeGeometry( points() ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers if ( vlayer->geometryType() == Qgis::Polygon ) { //ignore all current layer features as they should be reshaped too QMap<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures; ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); if ( geom.avoidIntersections( ignoreFeatures ) != 0 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } if ( geom.isGeosEmpty() ) //intersection removal might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } } vlayer->changeGeometry( f.id(), geom ); reshapeDone = true; } } } if ( reshapeDone ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } stopCapturing(); } }
//****************************************************************************************** void QRap::ReceivedLeftPoint(QgsPoint &Point) { cout << " in QRap::ReceivedLeftPoint(QgsPoint &Point) " << endl; if (mMouseType != CLEAN) { if (mMouseType == MOVESITE) { cout << " movesite " << endl; QMessageBox::information(mQGisIface->mainWindow(), "Q-Rap", "Wait: getting height data at point"); MoveSiteDialog((Point.y()),(Point.x())); mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); mQGisIface->mapCanvas()->refresh(); } else if (mMouseType == PLACESITE) { cout << " placesite " << endl; QMessageBox::information(mQGisIface->mainWindow(), "Q-Rap", "Wait: getting height data at point"); PlaceSiteDialog((Point.y()),(Point.x()),false); if (mMouseType!=MOVESITE) { mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); mQGisIface->mapCanvas()->refresh(); } } else if (mMouseType == SELECTSITE) { cout << " selectsite " << endl; PlaceSiteDialog((Point.y()),(Point.x()),true); if (mMouseType!=MOVESITE) { mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); } } if (mMouseType == LINK2) { cout << " Link2 " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); DesRubberBand(); PerformLink(); mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); } if (mMouseType == LINK1) { cout << " Link1 " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); mMouseType = LINK2; } if (mMouseType == AREA) { cout << " Area " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); } if (mMouseType == FILTERAREA) { cout << " Filter Area " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); } if (mMouseType == SELECTLINK) { cout << " Select Link " << endl; GetLink((Point.y()),(Point.x())); mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); } if (mMouseType == SPECTRAL) { cout << " spectral " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); } if (mMouseType == MULTILINK) { cout << " multilink " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); } if (mMouseType == OPTIMISATION) { cout << " optimisation " << endl; mPoints.append(Point); mRubberBand->addPoint(Point); } if (mMouseType == DELETESITE) { cout << "Deleting site we hope ..." << endl; cDeleteObject *Delete = new cDeleteObject(mQGisIface->mainWindow(), QgisGui::ModalDialogFlags); cout << "After delete object constructer" << endl; QString Lat = QString("%1").arg(Point.y()); QString Lon = QString("%1").arg(Point.x()); if (Delete->SetParameters(Lat,Lon,true,mQGisIface->mapCanvas()->scale())) { if (Delete->exec()==1) { mQGisIface->mapCanvas()->refresh(); } } mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); } if (mMouseType == DELETELINK) { cout << " delete link " << endl; cDeleteObject *Delete; Delete = new cDeleteObject(mQGisIface->mainWindow(), QgisGui::ModalDialogFlags); QString Lat = QString("%1").arg(Point.y()); QString Lon = QString("%1").arg(Point.x()); if (Delete->SetParameters(Lat,Lon,false,mQGisIface->mapCanvas()->scale())) { if (Delete->exec()==1) { mQGisIface->mapCanvas()->refresh(); } } mMouseType = CLEAN; mQGisIface->mapCanvas()->setCursor(Qt::OpenHandCursor); } } else cout << " clean " << endl; }
void QgsMapToolDigitizeFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer ); if ( !vlayer ) //if no given layer take the current from canvas vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QgsWkbTypes::Type layerWKBType = vlayer->wkbType(); QgsVectorDataProvider *provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), Qgis::Warning ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { if ( e->button() != Qt::LeftButton ) return; //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), Qgis::Warning ); return; } QgsPoint savePoint; //point in layer coordinates bool isMatchPointZ = false; try { QgsPoint fetchPoint; int res; res = fetchLayerPoint( e->mapPointMatch(), fetchPoint ); if ( QgsWkbTypes::hasZ( fetchPoint.wkbType() ) ) isMatchPointZ = true; if ( res == 0 ) { if ( isMatchPointZ ) savePoint = fetchPoint; else savePoint = QgsPoint( fetchPoint.x(), fetchPoint.y() ); } else { QgsPointXY layerPoint = toLayerCoordinates( vlayer, e->mapPoint() ); if ( isMatchPointZ ) savePoint = QgsPoint( QgsWkbTypes::PointZ, layerPoint.x(), layerPoint.y(), fetchPoint.z() ); else savePoint = QgsPoint( layerPoint.x(), layerPoint.y() ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } //only do the rest for provider with feature addition support //note that for the grass provider, this will return false since //grass provider has its own mechanism of feature addition if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) { QgsFeature f( vlayer->fields(), 0 ); QgsGeometry g; if ( layerWKBType == QgsWkbTypes::Point ) { g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) ); } else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) ) { g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) ); } else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) ) { g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint ); } else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) ) { QgsMultiPoint *mp = new QgsMultiPoint(); mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) ); g.set( mp ); } else { // if layer supports more types (mCheckGeometryType is false) g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) ); } if ( QgsWkbTypes::hasM( layerWKBType ) ) { g.get()->addMValue(); } f.setGeometry( g ); f.setValid( true ); digitized( f ); // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points cadDockWidget()->clearPoints(); } } // LINE AND POLYGON CAPTURING else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), Qgis::Warning ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), Qgis::Warning ); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string deleteTempRubberBand(); //lines: bail out if there are not at least two vertices if ( mode() == CaptureLine && size() < 2 ) { stopCapturing(); return; } //polygons: bail out if there are not at least two vertices if ( mode() == CapturePolygon && size() < 3 ) { stopCapturing(); return; } if ( mode() == CapturePolygon ) { closePolygon(); } //create QgsFeature with wkb representation std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) ); //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QList<QgsPointLocator::Match> snappingMatchesList; QgsCurve *curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); snappingMatchesList = snappingMatches(); } if ( mode() == CaptureLine ) { QgsGeometry g( curveToAdd ); f->setGeometry( g ); } else { QgsCurvePolygon *poly = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { poly = new QgsCurvePolygon(); } else { poly = new QgsPolygon(); } poly->setExteriorRing( curveToAdd ); QgsGeometry g( poly ); f->setGeometry( g ); QgsGeometry featGeom = f->geometry(); int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); f->setGeometry( featGeom ); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), Qgis::Critical ); stopCapturing(); return; } } f->setValid( true ); digitized( *f ); stopCapturing(); } } }
bool QgsHeatmapRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) { Q_UNUSED( layer ); Q_UNUSED( selected ); Q_UNUSED( drawVertexMarker ); if ( !context.painter() ) { return false; } if ( !feature.hasGeometry() || feature.geometry().type() != QgsWkbTypes::PointGeometry ) { //can only render point type return false; } double weight = 1.0; if ( !mWeightExpressionString.isEmpty() ) { QVariant value; if ( mWeightAttrNum == -1 ) { Q_ASSERT( mWeightExpression.data() ); value = mWeightExpression->evaluate( &context.expressionContext() ); } else { QgsAttributes attrs = feature.attributes(); value = attrs.value( mWeightAttrNum ); } bool ok = false; double evalWeight = value.toDouble( &ok ); if ( ok ) { weight = evalWeight; } } int width = context.painter()->device()->width() / mRenderQuality; int height = context.painter()->device()->height() / mRenderQuality; //transform geometry if required QgsGeometry geom = feature.geometry(); QgsCoordinateTransform xform = context.coordinateTransform(); if ( xform.isValid() ) { geom.transform( xform ); } //convert point to multipoint QgsMultiPoint multiPoint = convertToMultipoint( &geom ); //loop through all points in multipoint for ( QgsMultiPoint::const_iterator pointIt = multiPoint.constBegin(); pointIt != multiPoint.constEnd(); ++pointIt ) { QgsPoint pixel = context.mapToPixel().transform( *pointIt ); int pointX = pixel.x() / mRenderQuality; int pointY = pixel.y() / mRenderQuality; for ( int x = qMax( pointX - mRadiusPixels, 0 ); x < qMin( pointX + mRadiusPixels, width ); ++x ) { for ( int y = qMax( pointY - mRadiusPixels, 0 ); y < qMin( pointY + mRadiusPixels, height ); ++y ) { int index = y * width + x; if ( index >= mValues.count() ) { continue; } double distanceSquared = pow( pointX - x, 2.0 ) + pow( pointY - y, 2.0 ); if ( distanceSquared > mRadiusSquared ) { continue; } double score = weight * quarticKernel( sqrt( distanceSquared ), mRadiusPixels ); double value = mValues.at( index ) + score; if ( value > mCalculatedMaxValue ) { mCalculatedMaxValue = value; } mValues[ index ] = value; } } } mFeaturesRendered++; #if 0 //TODO - enable progressive rendering if ( mFeaturesRendered % 200 == 0 ) { renderImage( context ); } #endif return true; }
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); QgsFeatureList featList; // toLayerCoordinates will throw an exception for an 'invalid' point. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. try { // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } if ( featList.size() == 0 ) return false; foreach ( QgsFeature feat, featList ) { if ( layer->actions()->defaultAction() >= 0 ) { // define custom substitutions: layer id and clicked coords QMap<QString, QVariant> substitutionMap; substitutionMap.insert( "$layerid", layer->id() ); point = toLayerCoordinates( layer, point ); substitutionMap.insert( "$clickx", point.x() ); substitutionMap.insert( "$clicky", point.y() ); int actionIdx = layer->actions()->defaultAction(); layer->actions()->doAction( actionIdx, feat, &substitutionMap ); } else { QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer ); if ( mapLayerAction ) { mapLayerAction->triggerForFeature( layer, &feat ); } } } return true; }
QgsRectangle QgsPointDisplacementRenderer::searchRect( const QgsPoint& p ) const { return QgsRectangle( p.x() - mTolerance, p.y() - mTolerance, p.x() + mTolerance, p.y() + mTolerance ); }
double QgsPoint::sqrDist( const QgsPoint& other ) const { return sqrDist( other.x(), other.y() ); }
void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) { QStringList messages; // assume the layer is invalid until proven otherwise mLayerValid = false; mValid = false; mRescanRequired = false; clearInvalidLines(); // Initiallize indexes resetIndexes(); bool buildSpatialIndex = buildIndexes && mSpatialIndex != 0; // No point building a subset index if there is no geometry, as all // records will be included. bool buildSubsetIndex = buildIndexes && mBuildSubsetIndex && mGeomRep != GeomNone; if ( ! mFile->isValid() ) { // uri is invalid so the layer must be too... messages.append( tr( "File cannot be opened or delimiter parameters are not valid" ) ); reportErrors( messages ); QgsDebugMsg( "Delimited text source invalid - filename or delimiter parameters" ); return; } // Open the file and get number of rows, etc. We assume that the // file has a header row and process accordingly. Caller should make // sure that the delimited file is properly formed. if ( mGeomRep == GeomAsWkt ) { mWktFieldIndex = mFile->fieldIndex( mWktFieldName ); if ( mWktFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Wkt" ).arg( mWktFieldName ) ); } } else if ( mGeomRep == GeomAsXy ) { mXFieldIndex = mFile->fieldIndex( mXFieldName ); mYFieldIndex = mFile->fieldIndex( mYFieldName ); if ( mXFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "X" ).arg( mWktFieldName ) ); } if ( mYFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Y" ).arg( mWktFieldName ) ); } } if ( messages.size() > 0 ) { reportErrors( messages ); QgsDebugMsg( "Delimited text source invalid - missing geometry fields" ); return; } // Scan the entire file to determine // 1) the number of fields (this is handled by QgsDelimitedTextFile mFile // 2) the number of valid features. Note that the selection of valid features // should match the code in QgsDelimitedTextFeatureIterator // 3) the geometric extents of the layer // 4) the type of each field // // Also build subset and spatial indexes. QStringList parts; long nEmptyRecords = 0; long nBadFormatRecords = 0; long nIncompatibleGeometry = 0; long nInvalidGeometry = 0; long nEmptyGeometry = 0; mNumberFeatures = 0; mExtent = QgsRectangle(); QList<bool> isEmpty; QList<bool> couldBeInt; QList<bool> couldBeLongLong; QList<bool> couldBeDouble; while ( true ) { QgsDelimitedTextFile::Status status = mFile->nextRecord( parts ); if ( status == QgsDelimitedTextFile::RecordEOF ) break; if ( status != QgsDelimitedTextFile::RecordOk ) { nBadFormatRecords++; recordInvalidLine( tr( "Invalid record format at line %1" ) ); continue; } // Skip over empty records if ( recordIsEmpty( parts ) ) { nEmptyRecords++; continue; } // Check geometries are valid bool geomValid = true; if ( mGeomRep == GeomAsWkt ) { if ( mWktFieldIndex >= parts.size() || parts[mWktFieldIndex].isEmpty() ) { nEmptyGeometry++; geomValid = false; } else { // Get the wkt - confirm it is valid, get the type, and // if compatible with the rest of file, add to the extents QString sWkt = parts[mWktFieldIndex]; QgsGeometry *geom = 0; if ( !mWktHasPrefix && sWkt.indexOf( WktPrefixRegexp ) >= 0 ) mWktHasPrefix = true; if ( !mWktHasZM && sWkt.indexOf( WktZMRegexp ) >= 0 ) mWktHasZM = true; geom = geomFromWkt( sWkt, mWktHasPrefix, mWktHasZM ); if ( geom ) { QGis::WkbType type = geom->wkbType(); if ( type != QGis::WKBNoGeometry ) { if ( mGeometryType == QGis::UnknownGeometry || geom->type() == mGeometryType ) { mGeometryType = geom->type(); if ( mNumberFeatures == 0 ) { mNumberFeatures++; mWkbType = type; mExtent = geom->boundingBox(); } else { mNumberFeatures++; if ( geom->isMultipart() ) mWkbType = type; QgsRectangle bbox( geom->boundingBox() ); mExtent.combineExtentWith( &bbox ); } if ( buildSpatialIndex ) { QgsFeature f; f.setFeatureId( mFile->recordId() ); f.setGeometry( geom ); mSpatialIndex->insertFeature( f ); // Feature now has ownership of geometry, so set to null // here to avoid deleting twice. geom = 0; } } else { nIncompatibleGeometry++; geomValid = false; } } if ( geom ) delete geom; } else { geomValid = false; nInvalidGeometry++; recordInvalidLine( tr( "Invalid WKT at line %1" ) ); } } } else if ( mGeomRep == GeomAsXy ) { // Get the x and y values, first checking to make sure they // aren't null. QString sX = mXFieldIndex < parts.size() ? parts[mXFieldIndex] : ""; QString sY = mYFieldIndex < parts.size() ? parts[mYFieldIndex] : ""; if ( sX.isEmpty() && sY.isEmpty() ) { geomValid = false; nEmptyGeometry++; } else { QgsPoint pt; bool ok = pointFromXY( sX, sY, pt, mDecimalPoint, mXyDms ); if ( ok ) { if ( mNumberFeatures > 0 ) { mExtent.combineExtentWith( pt.x(), pt.y() ); } else { // Extent for the first point is just the first point mExtent.set( pt.x(), pt.y(), pt.x(), pt.y() ); mWkbType = QGis::WKBPoint; mGeometryType = QGis::Point; } mNumberFeatures++; if ( buildSpatialIndex && qIsFinite( pt.x() ) && qIsFinite( pt.y() ) ) { QgsFeature f; f.setFeatureId( mFile->recordId() ); f.setGeometry( QgsGeometry::fromPoint( pt ) ); mSpatialIndex->insertFeature( f ); } } else { geomValid = false; nInvalidGeometry++; recordInvalidLine( tr( "Invalid X or Y fields at line %1" ) ); } } } else { mWkbType = QGis::WKBNoGeometry; mNumberFeatures++; } if ( ! geomValid ) continue; if ( buildSubsetIndex ) mSubsetIndex.append( mFile->recordId() ); // If we are going to use this record, then assess the potential types of each colum for ( int i = 0; i < parts.size(); i++ ) { QString &value = parts[i]; // Ignore empty fields - spreadsheet generated CSV files often // have random empty fields at the end of a row if ( value.isEmpty() ) continue; // Expand the columns to include this non empty field if necessary while ( couldBeInt.size() <= i ) { isEmpty.append( true ); couldBeInt.append( false ); couldBeLongLong.append( false ); couldBeDouble.append( false ); } // If this column has been empty so far then initiallize it // for possible types if ( isEmpty[i] ) { isEmpty[i] = false; couldBeInt[i] = true; couldBeLongLong[i] = true; couldBeDouble[i] = true; } // Now test for still valid possible types for the field // Types are possible until first record which cannot be parsed if ( couldBeInt[i] ) { value.toInt( &couldBeInt[i] ); } if ( couldBeLongLong[i] && ! couldBeInt[i] ) { value.toLongLong( &couldBeLongLong[i] ); } if ( couldBeDouble[i] && ! couldBeLongLong[i] ) { if ( ! mDecimalPoint.isEmpty() ) { value.replace( mDecimalPoint, "." ); } value.toDouble( &couldBeDouble[i] ); } } } // Now create the attribute fields. Field types are integer by preference, // failing that double, failing that text. QStringList fieldNames = mFile->fieldNames(); mFieldCount = fieldNames.size(); attributeColumns.clear(); attributeFields.clear(); QString csvtMessage; QStringList csvtTypes = readCsvtFieldTypes( mFile->fileName(), &csvtMessage ); for ( int i = 0; i < fieldNames.size(); i++ ) { // Skip over WKT field ... don't want to display in attribute table if ( i == mWktFieldIndex ) continue; // Add the field index lookup for the column attributeColumns.append( i ); QVariant::Type fieldType = QVariant::String; QString typeName = "text"; if ( i < csvtTypes.size() ) { if ( csvtTypes[i] == "integer" ) { fieldType = QVariant::Int; typeName = "integer"; } else if ( csvtTypes[i] == "long" || csvtTypes[i] == "longlong" || csvtTypes[i] == "int8" ) { fieldType = QVariant::LongLong; //QVariant doesn't support long typeName = "longlong"; } else if ( csvtTypes[i] == "real" || csvtTypes[i] == "double" ) { fieldType = QVariant::Double; typeName = "double"; } } else if ( i < couldBeInt.size() ) { if ( couldBeInt[i] ) { fieldType = QVariant::Int; typeName = "integer"; } else if ( couldBeLongLong[i] ) { fieldType = QVariant::LongLong; typeName = "longlong"; } else if ( couldBeDouble[i] ) { fieldType = QVariant::Double; typeName = "double"; } } attributeFields.append( QgsField( fieldNames[i], fieldType, typeName ) ); } QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) ); QgsDebugMsg( "geometry type is: " + QString::number( mWkbType ) ); QgsDebugMsg( "feature count is: " + QString::number( mNumberFeatures ) ); QStringList warnings; if ( ! csvtMessage.isEmpty() ) warnings.append( csvtMessage ); if ( nBadFormatRecords > 0 ) warnings.append( tr( "%1 records discarded due to invalid format" ).arg( nBadFormatRecords ) ); if ( nEmptyGeometry > 0 ) warnings.append( tr( "%1 records discarded due to missing geometry definitions" ).arg( nEmptyGeometry ) ); if ( nInvalidGeometry > 0 ) warnings.append( tr( "%1 records discarded due to invalid geometry definitions" ).arg( nInvalidGeometry ) ); if ( nIncompatibleGeometry > 0 ) warnings.append( tr( "%1 records discarded due to incompatible geometry types" ).arg( nIncompatibleGeometry ) ); reportErrors( warnings ); // Decide whether to use subset ids to index records rather than simple iteration through all // If more than 10% of records are being skipped, then use index. (Not based on any experimentation, // could do with some analysis?) if ( buildSubsetIndex ) { long recordCount = mFile->recordCount(); recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR; mUseSubsetIndex = mSubsetIndex.size() < recordCount; if ( ! mUseSubsetIndex ) mSubsetIndex = QList<quintptr>(); } mUseSpatialIndex = buildSpatialIndex; mValid = mGeometryType != QGis::UnknownGeometry; mLayerValid = mValid; // If it is valid, then watch for changes to the file connect( mFile, SIGNAL( fileUpdated() ), this, SLOT( onFileUpdated() ) ); }
QgsVector::QgsVector( const QgsPoint &p ) : m_x( p.x() ), m_y( p.y() ) { }
bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform& ct, QgsRectangle &extent, QgsRectangle &r2 ) { bool split = false; try { #ifdef QGISDEBUG // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__); #endif // Split the extent into two if the source CRS is // geographic and the extent crosses the split in // geographic coordinates (usually +/- 180 degrees, // and is assumed to be so here), and draw each // extent separately. static const double SPLIT_COORD = 180.0; if ( ml->crs().isGeographic() ) { if ( ml->type() == QgsMapLayer::VectorLayer && !ct.destinationCrs().isGeographic() ) { // if we transform from a projected coordinate system check // check if transforming back roughly returns the input // extend - otherwise render the world. QgsRectangle extent1 = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsRectangle extent2 = ct.transformBoundingBox( extent1, QgsCoordinateTransform::ForwardTransform ); QgsDebugMsgLevel( QString( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" ) .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ) .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() ) .arg( fabs( 1.0 - extent2.width() / extent.width() ) ) .arg( fabs( 1.0 - extent2.height() / extent.height() ) ) , 3 ); if ( fabs( 1.0 - extent2.width() / extent.width() ) < 0.5 && fabs( 1.0 - extent2.height() / extent.height() ) < 0.5 ) { extent = extent1; } else { extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ); } } else { // Note: ll = lower left point QgsPoint ll = ct.transform( extent.xMinimum(), extent.yMinimum(), QgsCoordinateTransform::ReverseTransform ); // and ur = upper right point QgsPoint ur = ct.transform( extent.xMaximum(), extent.yMaximum(), QgsCoordinateTransform::ReverseTransform ); QgsDebugMsg( QString( "in:%1 (ll:%2 ur:%3)" ).arg( extent.toString(), ll.toString(), ur.toString() ) ); extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsDebugMsg( QString( "out:%1 (w:%2 h:%3)" ).arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ) ); if ( ll.x() > ur.x() ) { // the coordinates projected in reverse order than what one would expect. // we are probably looking at an area that includes longitude of 180 degrees. // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180) // so let's use (-180,180). This hopefully does not add too much overhead. It is // more straightforward than rendering with two separate extents and more consistent // for rendering, labeling and caching as everything is rendered just in one go extent.setXMinimum( -SPLIT_COORD ); extent.setXMaximum( SPLIT_COORD ); } } // TODO: the above rule still does not help if using a projection that covers the whole // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx. // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90, // but in fact the extent should cover the whole world. } else // can't cross 180 { if ( ct.destinationCrs().isGeographic() && ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 || extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) ) // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates. // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity. // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat, // but this seems like a safer choice. extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); else extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "Transform error caught" ); extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); r2 = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); } return split; }
int QgsPoint::onSegment( const QgsPoint& a, const QgsPoint& b ) const { //algorithm from 'graphics GEMS', A. Paeth: 'A Fast 2D Point-on-line test' if ( qAbs(( b.y() - a.y() ) *( m_x - a.x() ) - ( m_y - a.y() ) *( b.x() - a.x() ) ) >= qMax( qAbs( b.x() - a.x() ), qAbs( b.y() - a.y() ) ) ) { return 0; } if (( b.x() < a.x() && a.x() < m_x ) || ( b.y() < a.y() && a.y() < m_y ) ) { return 1; } if (( m_x < a.x() && a.x() < b.x() ) || ( m_y < a.y() && a.y() < b.y() ) ) { return 1; } if (( a.x() < b.x() && b.x() < m_x ) || ( a.y() < b.y() && b.y() < m_y ) ) { return 3; } if (( m_x < b.x() && b.x() < a.x() ) || ( m_y < b.y() && b.y() < a.y() ) ) { return 3; } return 2; }
QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext ) : QgsMapLayerRenderer( layer->id() ) , mRasterViewPort( nullptr ) , mPipe( nullptr ) , mContext( rendererContext ) , mFeedback( new Feedback( this ) ) { mPainter = rendererContext.painter(); const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel(); mMapToPixel = &theQgsMapToPixel; QgsMapToPixel mapToPixel = theQgsMapToPixel; if ( mapToPixel.mapRotation() ) { // unset rotation for the sake of local computations. // Rotation will be handled by QPainter later // TODO: provide a method of QgsMapToPixel to fetch map center // in geographical units QgsPoint center = mapToPixel.toMapCoordinates( mapToPixel.mapWidth() / 2.0, mapToPixel.mapHeight() / 2.0 ); mapToPixel.setMapRotation( 0, center.x(), center.y() ); } QgsRectangle myProjectedViewExtent; QgsRectangle myProjectedLayerExtent; if ( rendererContext.coordinateTransform().isValid() ) { QgsDebugMsgLevel( "coordinateTransform set -> project extents.", 4 ); try { myProjectedViewExtent = rendererContext.coordinateTransform().transformBoundingBox( rendererContext.extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedViewExtent.setMinimal(); } try { myProjectedLayerExtent = rendererContext.coordinateTransform().transformBoundingBox( layer->extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedLayerExtent.setMinimal(); } } else { QgsDebugMsgLevel( "coordinateTransform not set", 4 ); myProjectedViewExtent = rendererContext.extent(); myProjectedLayerExtent = layer->extent(); } // clip raster extent to view extent QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent ); if ( myRasterExtent.isEmpty() ) { QgsDebugMsg( "draw request outside view extent." ); // nothing to do return; } QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 ); QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 ); QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 ); QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 ); // // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings // relating to the size (in pixels and coordinate system units) of the raster part that is // in view in the map window. It also stores the origin. // //this is not a class level member because every time the user pans or zooms //the contents of the rasterViewPort will change mRasterViewPort = new QgsRasterViewPort(); mRasterViewPort->mDrawnExtent = myRasterExtent; if ( rendererContext.coordinateTransform().isValid() ) { mRasterViewPort->mSrcCRS = layer->crs(); mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs(); mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform().sourceDatumTransform(); mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform().destinationDatumTransform(); } else { mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mSrcDatumTransform = -1; mRasterViewPort->mDestDatumTransform = -1; } // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport) mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() ); mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() ); // align to output device grid, i.e. floor/ceil to integers // TODO: this should only be done if paint device is raster - screen, image // for other devices (pdf) it can have floating point origin // we could use floating point for raster devices as well, but respecting the // output device grid should make it more effective as the resampling is done in // the provider anyway mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) ); mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) ); mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) ); mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) ); // recalc myRasterExtent to aligned values myRasterExtent.set( mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(), mRasterViewPort->mBottomRightPoint.y() ), mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(), mRasterViewPort->mTopLeftPoint.y() ) ); //raster viewport top left / bottom right are already rounded to int mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ); mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ); //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because //mapToPixel.mapUnitsPerPixel() is less then 1, //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas() QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 ); // /\/\/\ - added to handle zoomed-in rasters // TODO R->mLastViewPort = *mRasterViewPort; // TODO: is it necessary? Probably WMS only? layer->dataProvider()->setDpi( 25.4 * rendererContext.scaleFactor() ); // copy the whole raster pipe! mPipe = new QgsRasterPipe( *layer->pipe() ); QgsRasterRenderer* rasterRenderer = mPipe->renderer(); if ( rasterRenderer ) layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() ); }
double QgsDistanceArea::computeDistanceBearing( const QgsPoint& p1, const QgsPoint& p2, double* course1, double* course2 ) { if ( p1.x() == p2.x() && p1.y() == p2.y() ) return 0; // ellipsoid double a = mSemiMajor; double b = mSemiMinor; double f = 1 / mInvFlattening; double p1_lat = DEG2RAD( p1.y() ), p1_lon = DEG2RAD( p1.x() ); double p2_lat = DEG2RAD( p2.y() ), p2_lon = DEG2RAD( p2.x() ); double L = p2_lon - p1_lon; double U1 = atan(( 1 - f ) * tan( p1_lat ) ); double U2 = atan(( 1 - f ) * tan( p2_lat ) ); double sinU1 = sin( U1 ), cosU1 = cos( U1 ); double sinU2 = sin( U2 ), cosU2 = cos( U2 ); double lambda = L; double lambdaP = 2 * M_PI; double sinLambda = 0; double cosLambda = 0; double sinSigma = 0; double cosSigma = 0; double sigma = 0; double alpha = 0; double cosSqAlpha = 0; double cos2SigmaM = 0; double C = 0; double tu1 = 0; double tu2 = 0; int iterLimit = 20; while ( qAbs( lambda - lambdaP ) > 1e-12 && --iterLimit > 0 ) { sinLambda = sin( lambda ); cosLambda = cos( lambda ); tu1 = ( cosU2 * sinLambda ); tu2 = ( cosU1 * sinU2 - sinU1 * cosU2 * cosLambda ); sinSigma = sqrt( tu1 * tu1 + tu2 * tu2 ); cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = atan2( sinSigma, cosSigma ); alpha = asin( cosU1 * cosU2 * sinLambda / sinSigma ); cosSqAlpha = cos( alpha ) * cos( alpha ); cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; C = f / 16 * cosSqAlpha * ( 4 + f * ( 4 - 3 * cosSqAlpha ) ); lambdaP = lambda; lambda = L + ( 1 - C ) * f * sin( alpha ) * ( sigma + C * sinSigma * ( cos2SigmaM + C * cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) ) ); } if ( iterLimit == 0 ) return -1; // formula failed to converge double uSq = cosSqAlpha * ( a * a - b * b ) / ( b * b ); double A = 1 + uSq / 16384 * ( 4096 + uSq * ( -768 + uSq * ( 320 - 175 * uSq ) ) ); double B = uSq / 1024 * ( 256 + uSq * ( -128 + uSq * ( 74 - 47 * uSq ) ) ); double deltaSigma = B * sinSigma * ( cos2SigmaM + B / 4 * ( cosSigma * ( -1 + 2 * cos2SigmaM * cos2SigmaM ) - B / 6 * cos2SigmaM * ( -3 + 4 * sinSigma * sinSigma ) * ( -3 + 4 * cos2SigmaM * cos2SigmaM ) ) ); double s = b * A * ( sigma - deltaSigma ); if ( course1 ) { *course1 = atan2( tu1, tu2 ); } if ( course2 ) { // PI is added to return azimuth from P2 to P1 *course2 = atan2( cosU1 * sinLambda, -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda ) + M_PI; } return s; }
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight ) { const QgsCurve *exterior = polygon.exteriorRing(); const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ); const int pCount = exterior->numPoints(); if ( pCount == 4 && polygon.numInteriorRings() == 0 ) { // polygon is a triangle - write vertices to the output data array without triangulation QgsPoint pt; QgsVertexId::VertexType vt; for ( int i = 0; i < 3; i++ ) { exterior->pointAt( i, pt, vt ); mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } if ( mAddBackFaces ) { // the same triangle with reversed order of coordinates and inverted normal for ( int i = 2; i >= 0; i-- ) { exterior->pointAt( i, pt, vt ); mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY; if ( mAddNormals ) mData << -pNormal.x() << -pNormal.z() << pNormal.y(); } } } else { if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) ) return; // this should not happen - pNormal should be normalized to unit length std::unique_ptr<QMatrix4x4> toNewBase, toOldBase; if ( pNormal != QVector3D( 0, 0, 1 ) ) { // this is not a horizontal plane - need to reproject the polygon to a new base so that // we can do the triangulation in a plane QVector3D pXVector, pYVector; _normalVectorToXYVectors( pNormal, pXVector, pYVector ); // so now we have three orthogonal unit vectors defining new base // let's build transform matrix. We actually need just a 3x3 matrix, // but Qt does not have good support for it, so using 4x4 matrix instead. toNewBase.reset( new QMatrix4x4( pXVector.x(), pXVector.y(), pXVector.z(), 0, pYVector.x(), pYVector.y(), pYVector.z(), 0, pNormal.x(), pNormal.y(), pNormal.z(), 0, 0, 0, 0, 0 ) ); // our 3x3 matrix is orthogonal, so for inverse we only need to transpose it toOldBase.reset( new QMatrix4x4( toNewBase->transposed() ) ); } const QgsPoint ptStart( exterior->startPoint() ); const QgsPoint pt0( QgsWkbTypes::PointZ, ptStart.x(), ptStart.y(), std::isnan( ptStart.z() ) ? 0 : ptStart.z() ); // subtract ptFirst from geometry for better numerical stability in triangulation // and apply new 3D vector base if the polygon is not horizontal std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get() ) ); if ( _minimum_distance_between_coordinates( *polygonNew ) < 0.001 ) { // when the distances between coordinates of input points are very small, // the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5 // Assuming that the coordinates should be in a projected CRS, we should be able // to simplify geometries that may cause problems and avoid possible crashes QgsGeometry polygonSimplified = QgsGeometry( polygonNew->clone() ).simplify( 0.001 ); const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() ); if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 ) { // Failed to fix that. It could be a really tiny geometry... or maybe they gave us // geometry in unprojected lat/lon coordinates QgsMessageLog::logMessage( QObject::tr( "geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr( "3D" ) ); return; } else { polygonNew.reset( polygonSimplifiedData->clone() ); } } if ( !_check_intersecting_rings( *polygonNew.get() ) ) { // skip the polygon - it would cause a crash inside poly2tri library QgsMessageLog::logMessage( QObject::tr( "polygon rings self-intersect or intersect each other - skipping" ), QObject::tr( "3D" ) ); return; } QList< std::vector<p2t::Point *> > polylinesToDelete; QHash<p2t::Point *, float> z; // polygon exterior std::vector<p2t::Point *> polyline; _ringToPoly2tri( polygonNew->exteriorRing(), polyline, z ); polylinesToDelete << polyline; std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( polyline ) ); // polygon holes for ( int i = 0; i < polygonNew->numInteriorRings(); ++i ) { std::vector<p2t::Point *> holePolyline; const QgsCurve *hole = polygonNew->interiorRing( i ); _ringToPoly2tri( hole, holePolyline, z ); cdt->AddHole( holePolyline ); polylinesToDelete << holePolyline; } // run triangulation and write vertices to the output data array try { cdt->Triangulate(); std::vector<p2t::Triangle *> triangles = cdt->GetTriangles(); for ( size_t i = 0; i < triangles.size(); ++i ) { p2t::Triangle *t = triangles[i]; for ( int j = 0; j < 3; ++j ) { p2t::Point *p = t->GetPoint( j ); QVector4D pt( p->x, p->y, z[p], 0 ); if ( toOldBase ) pt = *toOldBase * pt; const double fx = pt.x() - mOriginX + pt0.x(); const double fy = pt.y() - mOriginY + pt0.y(); const double fz = pt.z() + extrusionHeight + pt0.z(); mData << fx << fz << -fy; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } if ( mAddBackFaces ) { // the same triangle with reversed order of coordinates and inverted normal for ( int j = 2; j >= 0; --j ) { p2t::Point *p = t->GetPoint( j ); QVector4D pt( p->x, p->y, z[p], 0 ); if ( toOldBase ) pt = *toOldBase * pt; const double fx = pt.x() - mOriginX + pt0.x(); const double fy = pt.y() - mOriginY + pt0.y(); const double fz = pt.z() + extrusionHeight + pt0.z(); mData << fx << fz << -fy; if ( mAddNormals ) mData << -pNormal.x() << -pNormal.z() << pNormal.y(); } } } } catch ( ... ) { QgsMessageLog::logMessage( QObject::tr( "Triangulation failed. Skipping polygon…" ), QObject::tr( "3D" ) ); } for ( int i = 0; i < polylinesToDelete.count(); ++i ) qDeleteAll( polylinesToDelete[i] ); } // add walls if extrusion is enabled if ( extrusionHeight != 0 ) { _makeWalls( *exterior, false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); for ( int i = 0; i < polygon.numInteriorRings(); ++i ) _makeWalls( *polygon.interiorRing( i ), true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); } }
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 ); }
bool QgsMapToolLabel::rotationPoint( QgsPoint& pos, bool ignoreUpsideDown, bool rotatingUnpinned ) { QVector<QgsPoint> cornerPoints = mCurrentLabelPos.cornerPoints; if ( cornerPoints.size() < 4 ) { return false; } if ( mCurrentLabelPos.upsideDown && !ignoreUpsideDown ) { pos = cornerPoints.at( 2 ); } else { pos = cornerPoints.at( 0 ); } //alignment always center/center and rotation 0 for diagrams if ( mCurrentLabelPos.isDiagram ) { pos.setX( pos.x() + mCurrentLabelPos.labelRect.width() / 2.0 ); pos.setY( pos.y() + mCurrentLabelPos.labelRect.height() / 2.0 ); return true; } //adapt pos depending on data defined alignment QString haliString, valiString; currentAlignment( haliString, valiString ); // rotate unpinned labels (i.e. no hali/vali settings) as if hali/vali was Center/Half if ( rotatingUnpinned ) { haliString = "Center"; valiString = "Half"; } // QFont labelFont = labelFontCurrentFeature(); QFontMetricsF labelFontMetrics( mCurrentLabelPos.labelFont ); // NOTE: this assumes the label corner points comprise a rectangle and that the // CRS supports equidistant measurements to accurately determine hypotenuse QgsPoint cp_0 = cornerPoints.at( 0 ); QgsPoint cp_1 = cornerPoints.at( 1 ); QgsPoint cp_3 = cornerPoints.at( 3 ); // QgsDebugMsg( QString( "cp_0: x=%1, y=%2" ).arg( cp_0.x() ).arg( cp_0.y() ) ); // QgsDebugMsg( QString( "cp_1: x=%1, y=%2" ).arg( cp_1.x() ).arg( cp_1.y() ) ); // QgsDebugMsg( QString( "cp_3: x=%1, y=%2" ).arg( cp_3.x() ).arg( cp_3.y() ) ); double labelSizeX = qSqrt( cp_0.sqrDist( cp_1 ) ); double labelSizeY = qSqrt( cp_0.sqrDist( cp_3 ) ); double xdiff = 0; double ydiff = 0; if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 ) { xdiff = labelSizeX / 2.0; } else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 ) { xdiff = labelSizeX; } if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 ) { ydiff = labelSizeY; } else { double descentRatio = 1 / labelFontMetrics.ascent() / labelFontMetrics.height(); if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 ) { ydiff = labelSizeY * descentRatio; } else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 ) { ydiff = labelSizeY * 0.5 * ( 1 - descentRatio ); } } double angle = mCurrentLabelPos.rotation; double xd = xdiff * cos( angle ) - ydiff * sin( angle ); double yd = xdiff * sin( angle ) + ydiff * cos( angle ); if ( mCurrentLabelPos.upsideDown && !ignoreUpsideDown ) { pos.setX( pos.x() - xd ); pos.setY( pos.y() - yd ); } else { pos.setX( pos.x() + xd ); pos.setY( pos.y() + yd ); } return true; }
void QgsResidualPlotItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget ) { Q_UNUSED( itemStyle ); Q_UNUSED( pWidget ); if ( mGCPList.size() < 1 || !painter ) { return; } double widthMM = rect().width(); double heightMM = rect().height(); QPen enabledPen( QColor( 255, 0, 0, 255 ), 0.3, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ); QPen disabledPen( QColor( 255, 0, 0, 85 ), 0.2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ); QBrush enabledBrush( QColor( 255, 255, 255, 255 ) ); QBrush disabledBrush( QColor( 255, 255, 255, 127 ) ); //draw all points and collect minimal mm/pixel ratio double minMMPixelRatio = DBL_MAX; double mmPixelRatio = 1; painter->setRenderHint( QPainter::Antialiasing, true ); QgsGCPList::const_iterator gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { QgsPoint gcpCoords = ( *gcpIt )->pixelCoords(); double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; if (( *gcpIt )->isEnabled() ) { painter->setPen( enabledPen ); painter->setBrush( enabledBrush ); } else { painter->setPen( disabledPen ); painter->setBrush( disabledBrush ); } painter->drawRect( QRectF( gcpItemMMX - 0.5, gcpItemMMY - 0.5, 1, 1 ) ); drawText( painter, gcpItemMMX + 2, gcpItemMMY + 2, QString::number(( *gcpIt )->id() ), QFont() ); mmPixelRatio = maxMMToPixelRatioForGCP( *gcpIt, gcpItemMMX, gcpItemMMY ); if ( mmPixelRatio < minMMPixelRatio ) { minMMPixelRatio = mmPixelRatio; } } //draw residual arrows gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { QgsPoint gcpCoords = ( *gcpIt )->pixelCoords(); double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; if (( *gcpIt )->isEnabled() ) { painter->setPen( enabledPen ); } else { painter->setPen( disabledPen ); } QPointF p1( gcpItemMMX, gcpItemMMY ); QPointF p2( gcpItemMMX + ( *gcpIt )->residual().x() * minMMPixelRatio, gcpItemMMY + ( *gcpIt )->residual().y() * minMMPixelRatio ); painter->drawLine( p1, p2 ); painter->setBrush( QBrush( painter->pen().color() ) ); QgsComposerUtils::drawArrowHead( painter, p2.x(), p2.y(), QgsComposerUtils::angle( p1, p2 ), 1 ); } //draw scale bar double initialScaleBarWidth = rect().width() / 5; double scaleBarWidthUnits = rect().width() / 5 / minMMPixelRatio; //a simple method to round to next nice number int nDecPlaces; if ( scaleBarWidthUnits < 1 ) { nDecPlaces = -floor( log10( scaleBarWidthUnits ) ); scaleBarWidthUnits *= pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); scaleBarWidthUnits /= pow( 10.0, nDecPlaces ); } else { nDecPlaces = ( int )log10( scaleBarWidthUnits ); scaleBarWidthUnits /= pow( 10.0, nDecPlaces ); scaleBarWidthUnits = ( int )( scaleBarWidthUnits + 0.5 ); scaleBarWidthUnits *= pow( 10.0, nDecPlaces ); } initialScaleBarWidth = scaleBarWidthUnits * minMMPixelRatio; painter->setPen( QColor( 0, 0, 0 ) ); painter->drawLine( QPointF( 5, rect().height() - 5 ), QPointF( 5 + initialScaleBarWidth, rect().height() - 5 ) ); painter->drawLine( QPointF( 5, rect().height() - 5 ), QPointF( 5, rect().height() - 7 ) ); painter->drawLine( QPointF( 5 + initialScaleBarWidth, rect().height() - 5 ), QPointF( 5 + initialScaleBarWidth, rect().height() - 7 ) ); QFont scaleBarFont; scaleBarFont.setPointSize( 9 ); if ( mConvertScaleToMapUnits ) { drawText( painter, 5, rect().height() - 4 + fontAscentMillimeters( scaleBarFont ), QString( "%1 map units" ).arg( scaleBarWidthUnits ), QFont() ); } else { drawText( painter, 5, rect().height() - 4 + fontAscentMillimeters( scaleBarFont ), QString( "%1 pixels" ).arg( scaleBarWidthUnits ), QFont() ); } drawFrame( painter ); if ( isSelected() ) { drawSelectionBoxes( painter ); } }
double QgsDistanceArea::computeDistanceFlat( const QgsPoint& p1, const QgsPoint& p2 ) const { return sqrt(( p2.x() - p1.x() ) * ( p2.x() - p1.x() ) + ( p2.y() - p1.y() ) * ( p2.y() - p1.y() ) ); }
QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( QgsFeature *feature, QgsMapLayer *layer ) { // Calculate derived attributes and insert: // measure distance or area depending on geometry type QMap< QString, QString > derivedAttributes; // init distance/area calculator QString ellipsoid = QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ); QgsDistanceArea calc; calc.setEllipsoidalMode( mCanvas->hasCrsTransformEnabled() ); calc.setEllipsoid( ellipsoid ); calc.setSourceCrs( layer->crs().srsid() ); QGis::WkbType wkbType = QGis::WKBNoGeometry; QGis::GeometryType geometryType = QGis::NoGeometry; if ( feature->geometry() ) { geometryType = feature->geometry()->type(); wkbType = feature->geometry()->wkbType(); } if ( geometryType == QGis::Line ) { double dist = calc.measure( feature->geometry() ); QGis::UnitType myDisplayUnits; convertMeasurement( calc, dist, myDisplayUnits, false ); QString str = calc.textUnit( dist, 3, myDisplayUnits, false ); // dist and myDisplayUnits are out params derivedAttributes.insert( tr( "Length" ), str ); if ( wkbType == QGis::WKBLineString || wkbType == QGis::WKBLineString25D ) { // Add the start and end points in as derived attributes QgsPoint pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, feature->geometry()->asPolyline().first() ); str = QLocale::system().toString( pnt.x(), 'g', 10 ); derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str ); str = QLocale::system().toString( pnt.y(), 'g', 10 ); derivedAttributes.insert( tr( "firstY" ), str ); pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, feature->geometry()->asPolyline().last() ); str = QLocale::system().toString( pnt.x(), 'g', 10 ); derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str ); str = QLocale::system().toString( pnt.y(), 'g', 10 ); derivedAttributes.insert( tr( "lastY" ), str ); } } else if ( geometryType == QGis::Polygon ) { double area = calc.measure( feature->geometry() ); double perimeter = calc.measurePerimeter( feature->geometry() ); QGis::UnitType myDisplayUnits; convertMeasurement( calc, area, myDisplayUnits, true ); // area and myDisplayUnits are out params QString str = calc.textUnit( area, 3, myDisplayUnits, true ); derivedAttributes.insert( tr( "Area" ), str ); convertMeasurement( calc, perimeter, myDisplayUnits, false ); // perimeter and myDisplayUnits are out params str = calc.textUnit( perimeter, 3, myDisplayUnits, false ); derivedAttributes.insert( tr( "Perimeter" ), str ); } else if ( geometryType == QGis::Point && ( wkbType == QGis::WKBPoint || wkbType == QGis::WKBPoint25D ) ) { // Include the x and y coordinates of the point as a derived attribute QgsPoint pnt = mCanvas->mapRenderer()->layerToMapCoordinates( layer, feature->geometry()->asPoint() ); QString str = QLocale::system().toString( pnt.x(), 'g', 10 ); derivedAttributes.insert( "X", str ); str = QLocale::system().toString( pnt.y(), 'g', 10 ); derivedAttributes.insert( "Y", str ); } return derivedAttributes; }
QgsPoint::QgsPoint( const QgsPoint& p ) { m_x = p.x(); m_y = p.y(); }
bool QgsGeorefTransform::transformRasterToWorld( const QgsPoint &raster, QgsPoint &world ) { // flip y coordinate due to different CS orientation QgsPoint raster_flipped( raster.x(), -raster.y() ); return gdal_transform( raster_flipped, world, 0 ); }
void QgsGrassEditSplitLine::mouseClick( QgsPoint & point, Qt::MouseButton button ) { double thresh = e->threshold(); switch ( button ) { case Qt::LeftButton: // Split previously selected line if ( e->mSelectedLine > 0 ) { e->eraseDynamic(); e->eraseElement( e->mSelectedLine ); int type = e->mProvider->readLine( e->mPoints, e->mCats, e->mSelectedLine ); double xl, yl; Vect_line_distance( e->mPoints, e->mLastPoint.x(), e->mLastPoint.y(), 0.0, 0, &xl, &yl, NULL, NULL, NULL, NULL ); e->mPoints->n_points = e->mSelectedPart; Vect_append_point( e->mPoints, xl, yl, 0.0 ); e->mProvider->rewriteLine( e->mSelectedLine, type, e->mPoints, e->mCats ); e->updateSymb(); e->displayUpdated(); Vect_reset_line( e->mPoints ); Vect_append_point( e->mPoints, xl, yl, 0.0 ); for ( int i = e->mSelectedPart; i < e->mEditPoints->n_points; i++ ) { Vect_append_point( e->mPoints, e->mEditPoints->x[i], e->mEditPoints->y[i], 0.0 ); } e->mProvider->writeLine( type, e->mPoints, e->mCats ); e->updateSymb(); e->displayUpdated(); e->mSelectedLine = 0; Vect_reset_line( e->mEditPoints ); e->setCanvasPrompt( tr( "Select position on line" ), "", "" ); } else { // Select new/next line e->mSelectedLine = e->mProvider->findLine( point.x(), point.y(), GV_LINES, thresh ); if ( e->mSelectedLine ) // highlite { e->mProvider->readLine( e->mEditPoints, NULL, e->mSelectedLine ); e->displayElement( e->mSelectedLine, e->mSymb[QgsGrassEdit::SYMB_HIGHLIGHT], e->mSize ); double xl, yl; // nearest point on the line // Note first segment is 1! e->mSelectedPart = Vect_line_distance( e->mEditPoints, point.x(), point.y(), 0.0, 0, &xl, &yl, NULL, NULL, NULL, NULL ); e->displayDynamic( xl, yl, QgsVertexMarker::ICON_X, e->mSize ); e->setCanvasPrompt( tr( "Split the line" ), "", tr( "Release the line" ) ); } else { e->setCanvasPrompt( tr( "Select point on line" ), "", "" ); } } break; case Qt::RightButton: e->eraseDynamic(); e->displayElement( e->mSelectedLine, e->mSymb[e->mLineSymb[e->mSelectedLine]], e->mSize ); e->mSelectedLine = 0; Vect_reset_line( e->mEditPoints ); e->setCanvasPrompt( tr( "Select point on line" ), "", "" ); break; default: // ignore others break; } }