QString QgsCoordinateUtils::formatCoordinateForProject( const QgsPoint& point, const QgsCoordinateReferenceSystem& destCrs, int precision ) { QString format = QgsProject::instance()->readEntry( "PositionPrecision", "/DegreeFormat", "MU" ); QgsPoint geo = point; if ( format == "DM" || format == "DMS" || format == "D" ) { // degrees if ( destCrs.isValid() && !destCrs.geographicFlag() ) { // need to transform to geographic coordinates QgsCoordinateTransform ct( destCrs, QgsCoordinateReferenceSystem( GEOSRID ) ); try { geo = ct.transform( point ); } catch ( QgsCsException& ) { return QString(); } } if ( format == "DM" ) return geo.toDegreesMinutes( precision, true, true ); else if ( format == "DMS" ) return geo.toDegreesMinutesSeconds( precision, true, true ); else return geo.toString( precision ); } else { // coordinates in map units return point.toString( precision ); } }
double QgsDistanceArea::measureLine( const QgsPoint &p1, const QgsPoint &p2 ) const { double result; try { QgsPoint pp1 = p1, pp2 = p2; QgsDebugMsgLevel( QString( "Measuring from %1 to %2" ).arg( p1.toString( 4 ), p2.toString( 4 ) ), 3 ); if ( mEllipsoidalMode && ( mEllipsoid != GEO_NONE ) ) { QgsDebugMsgLevel( QString( "Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 ); QgsDebugMsgLevel( QString( "From proj4 : %1" ).arg( mCoordTransform->sourceCrs().toProj4() ), 4 ); QgsDebugMsgLevel( QString( "To proj4 : %1" ).arg( mCoordTransform->destCRS().toProj4() ), 4 ); pp1 = mCoordTransform->transform( p1 ); pp2 = mCoordTransform->transform( p2 ); QgsDebugMsgLevel( QString( "New points are %1 and %2, calculating..." ).arg( pp1.toString( 4 ), pp2.toString( 4 ) ), 4 ); result = computeDistanceBearing( pp1, pp2 ); } else { QgsDebugMsgLevel( "Cartesian calculation on canvas coordinates", 4 ); result = computeDistanceFlat( p1, p2 ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsMessageLog::logMessage( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate line length." ) ); result = 0.0; } QgsDebugMsgLevel( QString( "The result was %1" ).arg( result ), 3 ); return result; }
bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { bool res = true; if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); idPoint = toLayerCoordinates( layer, idPoint ); QString type; if ( layer->providerType() == "wms" ) { type = tr( "WMS layer" ); //if WMS layer does not cover the view origin, //we need to map the view pixel coordinates //to WMS layer pixel coordinates QgsRectangle viewExtent = mCanvas->extent(); QgsRectangle layerExtent = layer->extent(); double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); if ( mapUnitsPerPixel > 0 && viewExtent.intersects( layerExtent ) ) { double xMinView = viewExtent.xMinimum(); double yMaxView = viewExtent.yMaximum(); double xMinLayer = layerExtent.xMinimum(); double yMaxLayer = layerExtent.yMaximum(); idPoint.set( xMinView < xMinLayer ? floor( x - ( xMinLayer - xMinView ) / mapUnitsPerPixel ) : x, yMaxView > yMaxLayer ? floor( y - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ) : y ); attributes.insert( tr( "Feature info" ), layer->identifyAsHtml( idPoint ) ); } else { res = false; } } else { type = tr( "Raster" ); res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes ); } if ( res ) { derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() ); results()->addFeature( layer, type, attributes, derivedAttributes ); } return res; }
void QgsStatusBarCoordinatesWidget::showMouseCoordinates( const QgsPoint & p ) { if ( !mMapCanvas || mToggleExtentsViewButton->isChecked() ) { return; } if ( mMapCanvas->mapUnits() == QGis::Degrees ) { if ( !mMapCanvas->mapSettings().destinationCrs().isValid() ) return; QgsPoint geo = p; if ( !mMapCanvas->mapSettings().destinationCrs().geographicFlag() ) { QgsCoordinateTransform ct( mMapCanvas->mapSettings().destinationCrs(), QgsCoordinateReferenceSystem( GEOSRID ) ); geo = ct.transform( p ); } QString format = QgsProject::instance()->readEntry( "PositionPrecision", "/DegreeFormat", "D" ); if ( format == "DM" ) mLineEdit->setText( geo.toDegreesMinutes( mMousePrecisionDecimalPlaces ) ); else if ( format == "DMS" ) mLineEdit->setText( geo.toDegreesMinutesSeconds( mMousePrecisionDecimalPlaces ) ); else mLineEdit->setText( geo.toString( mMousePrecisionDecimalPlaces ) ); } else { mLineEdit->setText( p.toString( mMousePrecisionDecimalPlaces ) ); } if ( mLineEdit->width() > mLineEdit->minimumWidth() ) { mLineEdit->setMinimumWidth( mLineEdit->width() ); } }
void QgsMeasureTool::addPoint( QgsPoint &point ) { QgsDebugMsg( "point=" + point.toString() ); // don't add points with the same coordinates if ( mPoints.size() > 0 && point == mPoints[0] ) return; QgsPoint pnt( point ); mPoints.append( pnt ); mRubberBand->addPoint( point ); mDialog->addPoint( point ); }
QString QgsRasterProjector::cpToString() { QString myString; for ( int i = 0; i < mCPRows; i++ ) { if ( i > 0 ) myString += "\n"; for ( int j = 0; j < mCPCols; j++ ) { if ( j > 0 ) myString += " "; QgsPoint myPoint = mCPMatrix[i][j]; myString += myPoint.toString(); } } return myString; }
void QgsMeasureTool::addPoint( QgsPoint &point ) { QgsDebugMsg( "point=" + point.toString() ); int last = mPoints.size() - 1; // don't add points with the same coordinates if ( mPoints.size() > 1 && mPoints[ last ] == mPoints[ last - 1 ] ) return; QgsPoint pnt( point ); // Append point that we will be moving. mPoints.append( pnt ); mRubberBand->addPoint( point ); if ( ! mDone ) { mDialog->addPoint( point ); } }
void QgsMeasureTool::addPoint( QgsPoint &point ) { QgsDebugMsg( "point=" + point.toString() ); // don't add points with the same coordinates if ( !mPoints.isEmpty() && mPoints.last() == point ) { return; } QgsPoint pnt( point ); // Append point that we will be moving. mPoints.append( pnt ); mRubberBand->addPoint( point ); mRubberBandPoints->addPoint( point ); if ( ! mDone ) // Prevent the insertion of a new item in segments measure table { mDialog->addPoint( point ); } }
void QgsGeometryValidator::checkRingIntersections( int p0, int i0, const QgsPolyline &ring0, int p1, int i1, const QgsPolyline &ring1 ) { for ( int i = 0; !mStop && i < ring0.size() - 1; i++ ) { QgsVector v = ring0[i+1] - ring0[i]; for ( int j = 0; !mStop && j < ring1.size() - 1; j++ ) { QgsVector w = ring1[j+1] - ring1[j]; QgsPoint s; if ( intersectLines( ring0[i], v, ring1[j], w, s ) ) { double d = -distLine2Point( ring0[i], v.perpVector(), s ); if ( d >= 0 && d <= v.length() ) { d = -distLine2Point( ring1[j], w.perpVector(), s ); if ( d > 0 && d < w.length() && ring0[i+1] != ring1[j+1] && ring0[i+1] != ring1[j] && ring0[i+0] != ring1[j+1] && ring0[i+0] != ring1[j] ) { QString msg = QObject::tr( "segment %1 of ring %2 of polygon %3 intersects segment %4 of ring %5 of polygon %6 at %7" ) .arg( i0 ).arg( i ).arg( p0 ) .arg( i1 ).arg( j ).arg( p1 ) .arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } } } } }
QString QgsRasterProjector::cpToString() { QString myString; for ( int i = 0; i < mCPRows; i++ ) { if ( i > 0 ) myString += '\n'; for ( int j = 0; j < mCPCols; j++ ) { if ( j > 0 ) myString += " "; QgsPoint myPoint = mCPMatrix[i][j]; if ( mCPLegalMatrix[i][j] ) { myString += myPoint.toString(); } else { myString += "(-,-)"; } } } return myString; }
QString ProjectorData::cpToString() { QString myString; for ( int i = 0; i < mCPRows; i++ ) { if ( i > 0 ) myString += '\n'; for ( int j = 0; j < mCPCols; j++ ) { if ( j > 0 ) myString += QLatin1String( " " ); QgsPoint myPoint = mCPMatrix[i][j]; if ( mCPLegalMatrix[i][j] ) { myString += myPoint.toString(); } else { myString += QLatin1String( "(-,-)" ); } } } return myString; }
void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e ) { QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QGis::WkbType 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." ), QgsMessageBar::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() != QGis::Point && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } QgsPoint savePoint; //point in layer coordinates try { savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::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 = 0; if ( layerWKBType == QGis::WKBPoint || layerWKBType == QGis::WKBPoint25D ) { g = QgsGeometry::fromPoint( savePoint ); } else if ( layerWKBType == QGis::WKBMultiPoint || layerWKBType == QGis::WKBMultiPoint25D ) { g = QgsGeometry::fromMultiPoint( QgsMultiPoint() << savePoint ); } else { // if layer supports more types (mCheckGeometryType is false) g = QgsGeometry::fromPoint( savePoint ); } f.setGeometry( g ); f.setValid( true ); addFeature( vlayer, &f, false ); mCanvas->refresh(); } } // 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() != QGis::Line && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QGis::Polygon && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint() ); 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 ) { // 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 QScopedPointer< 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; QgsCurveV2* curveToAdd = 0; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } if ( mode() == CaptureLine ) { f->setGeometry( new QgsGeometry( curveToAdd ) ); } else { QgsCurvePolygonV2* poly = 0; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { poly = new QgsCurvePolygonV2(); } else { poly = new QgsPolygonV2(); } poly->setExteriorRing( curveToAdd ); f->setGeometry( new QgsGeometry( poly ) ); int avoidIntersectionsReturn = f->geometry()->avoidIntersections(); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } #if 0 else if ( avoidIntersectionsReturn == 2 ) //MH120131: disable this error message until there is a better way to cope with the single type / multi type problem { //bail out... emit messageEmitted( tr( "The feature could not be added because removing the polygon intersections would change the geometry type" ), QgsMessageBar::CRITICAL ); stopCapturing(); return; } #endif else if ( avoidIntersectionsReturn == 3 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL ); } if ( !f->constGeometry()->asWkb() ) //avoid intersection might have removed the whole geometry { QString reason; if ( avoidIntersectionsReturn != 2 ) { reason = tr( "The feature cannot be added because it's geometry is empty" ); } else { reason = tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ); } emit messageEmitted( reason, QgsMessageBar::CRITICAL ); stopCapturing(); return; } } if ( addFeature( vlayer, f.data(), false ) ) { //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); //use always topological editing for avoidIntersection. //Otherwise, no way to guarantee the geometries don't have a small gap in between. QStringList intersectionLayers = QgsProject::instance()->readListEntry( "Digitizing", "/AvoidIntersectionsList" ); bool avoidIntersection = !intersectionLayers.isEmpty(); if ( avoidIntersection ) //try to add topological points also to background layers { QStringList::const_iterator lIt = intersectionLayers.constBegin(); for ( ; lIt != intersectionLayers.constEnd(); ++lIt ) { QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( *lIt ); QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml ); //can only add topological points if background layer is editable... if ( vl && vl->geometryType() == QGis::Polygon && vl->isEditable() ) { vl->addTopologicalPoints( f->constGeometry() ); } } } else if ( topologicalEditing ) { vlayer->addTopologicalPoints( f->constGeometry() ); } } stopCapturing(); } } }
double QgsCoordinateFormat::getHeightAtPos( const QgsPoint& p, const QgsCoordinateReferenceSystem& crs, QGis::UnitType unit, QString* errMsg ) { QString layerid = QgsProject::instance()->readEntry( "Heightmap", "layer" ); QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerid ); if ( !layer || layer->type() != QgsMapLayer::RasterLayer ) { if ( errMsg ) *errMsg = tr( "No heightmap is defined in the project." ), tr( "Right-click a raster layer in the layer tree and select it to be used as heightmap." ); return 0; } QString rasterFile = layer->source(); GDALDatasetH raster = GDALOpen( rasterFile.toLocal8Bit().data(), GA_ReadOnly ); if ( !raster ) { if ( errMsg ) *errMsg = tr( "Failed to open raster file: %1" ).arg( rasterFile ); return 0; } double gtrans[6] = {}; if ( GDALGetGeoTransform( raster, >rans[0] ) != CE_None ) { if ( errMsg ) *errMsg = tr( "Failed to get raster geotransform" ); GDALClose( raster ); return 0; } QString proj( GDALGetProjectionRef( raster ) ); const QgsCoordinateReferenceSystem& rasterCrs = QgsCRSCache::instance()->crsByWkt( proj ); if ( !rasterCrs.isValid() ) { if ( errMsg ) *errMsg = tr( "Failed to get raster CRS" ); GDALClose( raster ); return 0; } GDALRasterBandH band = GDALGetRasterBand( raster, 1 ); if ( !raster ) { if ( errMsg ) *errMsg = tr( "Failed to open raster band 0" ); GDALClose( raster ); return 0; } // Get vertical unit QGis::UnitType vertUnit = strcmp( GDALGetRasterUnitType( band ), "ft" ) == 0 ? QGis::Feet : QGis::Meters; // Transform geo position to raster CRS QgsPoint pRaster = QgsCoordinateTransformCache::instance()->transform( crs.authid(), rasterCrs.authid() )->transform( p ); QgsDebugMsg( QString( "Transform %1 from %2 to %3 gives %4" ).arg( p.toString() ) .arg( crs.authid() ).arg( rasterCrs.authid() ).arg( pRaster.toString() ) ); // Transform raster geo position to pixel coordinates double row = ( -gtrans[0] * gtrans[4] + gtrans[1] * gtrans[3] - gtrans[1] * pRaster.y() + gtrans[4] * pRaster.x() ) / ( gtrans[2] * gtrans[4] - gtrans[1] * gtrans[5] ); double col = ( -gtrans[0] * gtrans[5] + gtrans[2] * gtrans[3] - gtrans[2] * pRaster.y() + gtrans[5] * pRaster.x() ) / ( gtrans[1] * gtrans[5] - gtrans[2] * gtrans[4] ); double pixValues[4] = {}; if ( CE_None != GDALRasterIO( band, GF_Read, qFloor( col ), qFloor( row ), 2, 2, &pixValues[0], 2, 2, GDT_Float64, 0, 0 ) ) { if ( errMsg ) *errMsg = tr( "Failed to read pixel values" ); GDALClose( raster ); return 0; } GDALClose( raster ); // Interpolate values double lambdaR = row - qFloor( row ); double lambdaC = col - qFloor( col ); double value = ( pixValues[0] * ( 1. - lambdaC ) + pixValues[1] * lambdaC ) * ( 1. - lambdaR ) + ( pixValues[2] * ( 1. - lambdaC ) + pixValues[3] * lambdaC ) * ( lambdaR ); if ( rasterCrs.mapUnits() != unit ) { value *= QGis::fromUnitToUnitFactor( vertUnit, unit ); } return value; }
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; }
void QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e ) { QgsDebugMsg( "entered." ); QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } QGis::WkbType layerWKBType = vlayer->wkbType(); QgsVectorDataProvider* provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { QMessageBox::information( 0, tr( "Layer cannot be added to" ), tr( "The data provider for this layer does not support the addition of features." ) ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QGis::Point ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture point' tool on this vector layer" ) ); return; } QgsPoint idPoint; //point in map coordinates QList<QgsSnappingResult> snapResults; QgsPoint savePoint; //point in layer coordinates if ( mSnapper.snapToBackgroundLayers( e->pos(), snapResults ) == 0 ) { idPoint = snapPointFromResults( snapResults, e->pos() ); try { savePoint = toLayerCoordinates( vlayer, idPoint ); QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QMessageBox::information( 0, tr( "Coordinate transform error" ), tr( "Cannot transform the point to the layers coordinate system" ) ); 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 = new QgsFeature( 0, "WKBPoint" ); QgsGeometry *g = 0; if ( layerWKBType == QGis::WKBPoint || layerWKBType == QGis::WKBPoint25D ) { g = QgsGeometry::fromPoint( savePoint ); } else if ( layerWKBType == QGis::WKBMultiPoint || layerWKBType == QGis::WKBMultiPoint25D ) { g = QgsGeometry::fromMultiPoint( QgsMultiPoint() << savePoint ); } f->setGeometry( g ); vlayer->beginEditCommand( tr( "Feature added" ) ); if ( addFeature( vlayer, f ) ) { vlayer->endEditCommand(); } else { delete f; vlayer->destroyEditCommand(); } mCanvas->refresh(); } } else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QGis::Line ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture line' tool on this vector layer" ) ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QGis::Polygon ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture polygon' tool on this vector layer" ) ); return; } //add point to list and to rubber band int error = addVertex( e->pos() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation QMessageBox::information( 0, tr( "Coordinate transform error" ), tr( "Cannot transform the point to the layers coordinate system" ) ); return; } if ( e->button() == Qt::LeftButton ) { startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string //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; } //create QgsFeature with wkb representation QgsFeature* f = new QgsFeature( 0, "WKBLineString" ); QgsGeometry *g; if ( mode() == CaptureLine ) { if ( layerWKBType == QGis::WKBLineString || layerWKBType == QGis::WKBLineString25D ) { g = QgsGeometry::fromPolyline( points().toVector() ); } else if ( layerWKBType == QGis::WKBMultiLineString || layerWKBType == QGis::WKBMultiLineString25D ) { g = QgsGeometry::fromMultiPolyline( QgsMultiPolyline() << points().toVector() ); } else { QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) ); stopCapturing(); return; //unknown wkbtype } f->setGeometry( g ); } else // polygon { if ( layerWKBType == QGis::WKBPolygon || layerWKBType == QGis::WKBPolygon25D ) { g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() ); } else if ( layerWKBType == QGis::WKBMultiPolygon || layerWKBType == QGis::WKBMultiPolygon25D ) { g = QgsGeometry::fromMultiPolygon( QgsMultiPolygon() << ( QgsPolygon() << points().toVector() ) ); } else { QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) ); stopCapturing(); return; //unknown wkbtype } if ( !g ) { stopCapturing(); delete f; return; // invalid geometry; one possibility is from duplicate points } f->setGeometry( g ); int avoidIntersectionsReturn = f->geometry()->avoidIntersections(); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } #if 0 else if ( avoidIntersectionsReturn == 2 ) //MH120131: disable this error message until there is a better way to cope with the single type / multi type problem { //bail out... QMessageBox::critical( 0, tr( "Error" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type" ) ); delete f; stopCapturing(); return; } #endif else if ( avoidIntersectionsReturn == 3 ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error was reported during intersection removal" ) ); } } vlayer->beginEditCommand( tr( "Feature added" ) ); if ( addFeature( vlayer, f ) ) { //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); if ( topologicalEditing ) { vlayer->addTopologicalPoints( f->geometry() ); } vlayer->endEditCommand(); } else { delete f; vlayer->destroyEditCommand(); } stopCapturing(); } } }
bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); 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(); QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString(); 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 ); layer->select( layer->pendingAllAttributesList(), r, true, true ); QgsFeature f; while ( layer->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() ) ); } // init distance/area calculator QgsDistanceArea calc; if ( !featureList.count() == 0 ) { calc.setProjectionsEnabled( mCanvas->hasCrsTransformEnabled() ); // project? calc.setEllipsoid( ellipsoid ); calc.setSourceCrs( layer->crs().srsid() ); } QgsFeatureList::iterator f_it = featureList.begin(); for ( ; f_it != featureList.end(); ++f_it ) { featureCount++; QgsFeatureId fid = f_it->id(); QMap<QString, QString> derivedAttributes; // Calculate derived attributes and insert: // measure distance or area depending on geometry type if ( layer->geometryType() == QGis::Line ) { double dist = calc.measure( f_it->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 ( f_it->geometry()->wkbType() == QGis::WKBLineString || f_it->geometry()->wkbType() == QGis::WKBLineString25D ) { // Add the start and end points in as derived attributes str = QLocale::system().toString( f_it->geometry()->asPolyline().first().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( f_it->geometry()->asPolyline().first().y(), 'g', 10 ); derivedAttributes.insert( tr( "firstY" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().last().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( f_it->geometry()->asPolyline().last().y(), 'g', 10 ); derivedAttributes.insert( tr( "lastY" ), str ); } } else if ( layer->geometryType() == QGis::Polygon ) { double area = calc.measure( f_it->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 ); } else if ( layer->geometryType() == QGis::Point && ( f_it->geometry()->wkbType() == QGis::WKBPoint || f_it->geometry()->wkbType() == QGis::WKBPoint25D ) ) { // Include the x and y coordinates of the point as a derived attribute QString str; str = QLocale::system().toString( f_it->geometry()->asPoint().x(), 'g', 10 ); derivedAttributes.insert( "X", str ); str = QLocale::system().toString( f_it->geometry()->asPoint().y(), 'g', 10 ); derivedAttributes.insert( "Y", str ); } derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) ); results()->addFeature( layer, *f_it, derivedAttributes ); } 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 QgsGeometryValidator::validatePolyline( int i, QgsPolyline line, bool ring ) { if ( ring ) { if ( line.size() < 4 ) { QString msg = QObject::tr( "ring %1 with less than four points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } if ( line[0] != line[ line.size()-1 ] ) { QString msg = QObject::tr( "ring %1 not closed" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } } else if ( line.size() < 2 ) { QString msg = QObject::tr( "line %1 with less than two points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } int j = 0; while ( j < line.size() - 1 ) { int n = 0; while ( j < line.size() - 1 && line[j] == line[j+1] ) { line.remove( j ); n++; } if ( n > 0 ) { QString msg = QObject::tr( "line %1 contains %n duplicate node(s) at %2", "number of duplicate nodes", n ).arg( i ).arg( j ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, line[j] ) ); mErrorCount++; } j++; } for ( j = 0; !mStop && j < line.size() - 3; j++ ) { QgsVector v = line[j+1] - line[j]; double vl = v.length(); int n = ( j == 0 && ring ) ? line.size() - 2 : line.size() - 1; for ( int k = j + 2; !mStop && k < n; k++ ) { QgsVector w = line[k+1] - line[k]; QgsPoint s; if ( !intersectLines( line[j], v, line[k], w, s ) ) continue; double d = 0.0; try { d = -distLine2Point( line[j], v.perpVector(), s ); } catch ( QgsException & e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d < 0 || d > vl ) continue; try { d = -distLine2Point( line[k], w.perpVector(), s ); } catch ( QgsException & e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d <= 0 || d >= w.length() ) continue; QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4" ).arg( j ).arg( k ).arg( i ).arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } }
bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsPoint& point ) { if ( !layer || !layer->hasGeometryType() ) return false; if ( layer->hasScaleBasedVisibility() && ( layer->minimumScale() > mCanvas->mapSettings().scale() || layer->maximumScale() <= mCanvas->mapSettings().scale() ) ) { QgsDebugMsg( "Out of scale limits" ); return false; } QApplication::setOverrideCursor( Qt::WaitCursor ); QMap< QString, QString > commonDerivedAttributes; commonDerivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); 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 = 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 ) ) 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; QgsRenderContext context( QgsRenderContext::fromMapSettings( mCanvas->mapSettings() ) ); context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); QgsFeatureRendererV2* renderer = layer->rendererV2(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale for scale dependent visibility (rule based) renderer->startRender( context, layer->fields() ); filter = renderer->capabilities() & QgsFeatureRendererV2::Filter; } for ( ; f_it != featureList.end(); ++f_it ) { QMap< QString, QString > derivedAttributes = commonDerivedAttributes; QgsFeatureId fid = f_it->id(); context.expressionContext().setFeature( *f_it ); if ( filter && !renderer->willRenderFeature( *f_it, context ) ) continue; featureCount++; derivedAttributes.unite( featureDerivedAttributes( &( *f_it ), layer, toLayerCoordinates( layer, point ) ) ); 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( context ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); QApplication::restoreOverrideCursor(); return featureCount > 0; }
bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { bool res = true; if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider || !( dprovider->capabilities() & QgsRasterDataProvider::Identify ) ) { return false; } QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); try { idPoint = toLayerCoordinates( layer, idPoint ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "idPoint = %1 %2" ).arg( idPoint.x() ).arg( idPoint.y() ) ); if ( !layer->extent().contains( idPoint ) ) return false; QgsRectangle viewExtent = mCanvas->extent(); QMap< QString, QString > attributes, derivedAttributes; // 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 ); attributes = dprovider->identify( idPoint ); } 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 ); double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); // 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 ) ); attributes = dprovider->identify( idPoint, viewExtent, width, height ); } QString type; type = tr( "Raster" ); if ( attributes.size() > 0 ) { derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() ); results()->addFeature( layer, type, attributes, derivedAttributes ); } return res; }
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; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle& viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPoint pointInCanvasCrs = point; 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; QgsRaster::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 = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } 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 ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { Q_FOREACH ( int bandNo, values.keys() ) { QString valueString; if ( values.value( bandNo ).isNull() ) { valueString = tr( "no data" ); } else { double value = values.value( bandNo ).toDouble(); 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 QgsMapToolViewshed::drawFinished() { QString layerid = QgsProject::instance()->readEntry( "Heightmap", "layer" ); QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerid ); if ( !layer || layer->type() != QgsMapLayer::RasterLayer ) { QgisApp::instance()->messageBar()->pushMessage( tr( "No heightmap is defined in the project." ), tr( "Right-click a raster layer in the layer tree and select it to be used as heightmap." ), QgsMessageBar::INFO, 10 ); reset(); return; } QgsCoordinateReferenceSystem canvasCrs = canvas()->mapSettings().destinationCrs(); double curRadius; QgsPoint center; double trash; getPart( 0, center, curRadius, trash, trash ); QGis::UnitType measureUnit = canvasCrs.mapUnits(); QgsDistanceArea().convertMeasurement( curRadius, measureUnit, QGis::Meters, false ); QgsViewshedDialog viewshedDialog( curRadius ); connect( &viewshedDialog, SIGNAL( radiusChanged( double ) ), this, SLOT( adjustRadius( double ) ) ); if ( viewshedDialog.exec() == QDialog::Rejected ) { reset(); return; } QString outputFileName = QString( "viewshed_%1,%2.tif" ).arg( center.x() ).arg( center.y() ); QString outputFile = QgsTemporaryFile::createNewFile( outputFileName ); QVector<QgsPoint> filterRegion; QgsPolygon poly = QgsGeometry( getRubberBand()->geometry()->clone() ).asPolygon(); if ( !poly.isEmpty() ) { filterRegion = poly.front(); } getPart( 0, center, curRadius, trash, trash ); if ( mCanvas->mapSettings().mapUnits() == QGis::Degrees ) { // Need to compute radius in meters QgsDistanceArea da; da.setSourceCrs( mCanvas->mapSettings().destinationCrs() ); da.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); da.setEllipsoidalMode( mCanvas->mapSettings().hasCrsTransformEnabled() ); curRadius = da.measureLine( center, QgsPoint( center.x() + curRadius, center.y() ) ); QGis::UnitType measureUnits = mCanvas->mapSettings().mapUnits(); da.convertMeasurement( curRadius, measureUnits, QGis::Meters, false ); } double heightConv = QGis::fromUnitToUnitFactor( QgsCoordinateFormat::instance()->getHeightDisplayUnit(), QGis::Meters ); QProgressDialog p( tr( "Calculating viewshed..." ), tr( "Abort" ), 0, 0 ); p.setWindowTitle( tr( "Viewshed" ) ); p.setWindowModality( Qt::ApplicationModal ); bool displayVisible = viewshedDialog.getDisplayMode() == QgsViewshedDialog::DisplayVisibleArea; int accuracyFactor = viewshedDialog.getAccuracyFactor(); QApplication::setOverrideCursor( Qt::WaitCursor ); bool success = QgsViewshed::computeViewshed( layer->source(), outputFile, "GTiff", center, canvasCrs, viewshedDialog.getObserverHeight() * heightConv, viewshedDialog.getTargetHeight() * heightConv, viewshedDialog.getHeightRelativeToGround(), curRadius, QGis::Meters, filterRegion, displayVisible, accuracyFactor, &p ); QApplication::restoreOverrideCursor(); if ( success ) { QgsRasterLayer* layer = new QgsRasterLayer( outputFile, tr( "Viewshed [%1]" ).arg( center.toString() ) ); QgsColorRampShader* rampShader = new QgsColorRampShader(); if ( displayVisible ) { QList<QgsColorRampShader::ColorRampItem> colorRampItems = QList<QgsColorRampShader::ColorRampItem>() << QgsColorRampShader::ColorRampItem( 0, QColor( 0, 0, 0, 0 ), "" ) << QgsColorRampShader::ColorRampItem( 255, QColor( 0, 255, 0 ), tr( "Visible" ) ); rampShader->setColorRampItemList( colorRampItems ); } else { QList<QgsColorRampShader::ColorRampItem> colorRampItems = QList<QgsColorRampShader::ColorRampItem>() << QgsColorRampShader::ColorRampItem( 0, QColor( 0, 0, 0, 0 ), "" ) << QgsColorRampShader::ColorRampItem( 0, QColor( 255, 0, 0 ), tr( "Invisible" ) ); rampShader->setColorRampItemList( colorRampItems ); } QgsRasterShader* shader = new QgsRasterShader(); shader->setRasterShaderFunction( rampShader ); QgsSingleBandPseudoColorRenderer* renderer = new QgsSingleBandPseudoColorRenderer( 0, 1, shader ); layer->setRenderer( renderer ); QgsMapLayerRegistry::instance()->addMapLayer( layer ); QgsPinAnnotationItem* pin = new QgsPinAnnotationItem( canvas() ); pin->setMapPosition( center, canvasCrs ); pin->setItemFlags( pin->itemFlags() | QgsAnnotationItem::ItemMapPositionLocked ); QgisApp::instance()->itemCouplingManager()->addCoupling( layer, pin ); } else { QMessageBox::critical( 0, tr( "Error" ), tr( "Failed to compute viewshed." ) ); } reset(); }