Compare commits

...

4 Commits

Author SHA1 Message Date
Jorge Gustavo Rocha
18e38abd84
Merge fdae3f318e388bbc42701bfc6ba5fd9438185dc3 into d1528ee83d3173f504e5208dd8651dd3baf6a9da 2025-06-30 02:58:45 +02:00
Jorge Gustavo Rocha
fdae3f318e Clean up 2025-06-11 20:55:39 +01:00
Jorge Gustavo Rocha
b3afdad933 Compute vector values 2025-06-11 19:22:45 +01:00
Jorge Gustavo Rocha
0293fe42e2 Initial support for GetFeatureInfo requests over Mesh layers 2025-05-14 00:17:39 +01:00
4 changed files with 206 additions and 35 deletions

View File

@ -542,6 +542,27 @@ bool QgsMeshLayer::isFaceActive( const QgsMeshDatasetIndex &index, int faceIndex
return mDatasetGroupStore->isFaceActive( index, faceIndex ); return mDatasetGroupStore->isFaceActive( index, faceIndex );
} }
QgsMeshDatasetValue QgsMeshLayer::datasetValueUncached( const QgsRenderContext &renderContext, const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QgsMeshDatasetValue value;
const QgsTriangularMesh *mesh = triangularMesh();
if ( !mesh )
{
updateTriangularMesh( renderContext.coordinateTransform() );
mesh = triangularMesh();
}
if ( mesh && index.isValid() )
{
value = datasetValue( index, point, searchRadius );
}
return value;
}
QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const QgsMeshDatasetValue QgsMeshLayer::datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius ) const
{ {
QGIS_PROTECT_QOBJECT_THREAD_ACCESS QGIS_PROTECT_QOBJECT_THREAD_ACCESS

View File

@ -522,6 +522,7 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer, public QgsAbstractProfileSo
* \since QGIS 3.4 * \since QGIS 3.4
*/ */
QgsMeshDatasetValue datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius = 0 ) const; QgsMeshDatasetValue datasetValue( const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius = 0 ) const;
QgsMeshDatasetValue datasetValueUncached( const QgsRenderContext &renderContext, const QgsMeshDatasetIndex &index, const QgsPointXY &point, double searchRadius = 0 );
/** /**
* Returns the 3d values of stacked 3d mesh defined by the given point * Returns the 3d values of stacked 3d mesh defined by the given point

View File

@ -38,6 +38,8 @@
#include "qgsrasteridentifyresult.h" #include "qgsrasteridentifyresult.h"
#include "qgsrasterlayer.h" #include "qgsrasterlayer.h"
#include "qgsrasterrenderer.h" #include "qgsrasterrenderer.h"
#include "qgsmeshlayer.h"
#include "qgsmeshlayertemporalproperties.h"
#include "qgsscalecalculator.h" #include "qgsscalecalculator.h"
#include "qgsmaplayertemporalproperties.h" #include "qgsmaplayertemporalproperties.h"
#include "qgscoordinatereferencesystem.h" #include "qgscoordinatereferencesystem.h"
@ -1698,7 +1700,8 @@ namespace QgsWms
break; break;
} }
} }
else
if ( layer->type() == Qgis::LayerType::Raster )
{ {
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer ); QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer );
if ( !rasterLayer ) if ( !rasterLayer )
@ -1719,10 +1722,16 @@ namespace QgsWms
layerElement = result.createElement( QStringLiteral( "gml:featureMember" ) /*wfs:FeatureMember*/ ); layerElement = result.createElement( QStringLiteral( "gml:featureMember" ) /*wfs:FeatureMember*/ );
getFeatureInfoElement.appendChild( layerElement ); getFeatureInfoElement.appendChild( layerElement );
} }
( void ) featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, renderContext, result, layerElement, version ); ( void ) featureInfoFromRasterLayer( rasterLayer, mapSettings, &layerInfoPoint, renderContext, result, layerElement, version );
} }
break;
if ( layer->type() == Qgis::LayerType::Mesh )
{
QgsMeshLayer *meshLayer = qobject_cast<QgsMeshLayer *>( layer );
QgsPointXY layerInfoPoint = mapSettings.mapToLayerCoordinates( layer, *( infoPoint.get() ) );
( void ) featureInfoFromMeshLayer( meshLayer, mapSettings, &layerInfoPoint, renderContext, result, layerElement, version );
}
} }
} }
if ( !validLayer && !mContext.isValidLayer( queryLayer ) && !mContext.isValidGroup( queryLayer ) ) if ( !validLayer && !mContext.isValidLayer( queryLayer ) && !mContext.isValidGroup( queryLayer ) )
@ -2165,6 +2174,141 @@ namespace QgsWms
featureElem.appendChild( attributeElement ); featureElem.appendChild( attributeElement );
} }
bool QgsRenderer::featureInfoFromMeshLayer( QgsMeshLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const
{
Q_UNUSED( version )
Q_UNUSED( mapSettings )
if ( !infoPoint || !layer || !layer->dataProvider() )
{
return false;
}
bool isTemporal = layer->temporalProperties()->isActive();
QgsDateTimeRange range, layerRange;
const QString dateFormat = QStringLiteral( "yyyy-MM-ddTHH:mm:ss" );
QList<QgsMeshDatasetIndex> datasetIndexList;
int activeScalarGroup = layer->rendererSettings().activeScalarDatasetGroup();
int activeVectorGroup = layer->rendererSettings().activeVectorDatasetGroup();
const QList<int> allGroup = layer->enabledDatasetGroupsIndexes();
if ( isTemporal )
{
range = renderContext.temporalRange();
layerRange = static_cast<QgsMeshLayerTemporalProperties *>( layer->temporalProperties() )->timeExtent();
if ( activeScalarGroup >= 0 )
{
QgsMeshDatasetIndex indice;
indice = layer->activeScalarDatasetAtTime( range );
datasetIndexList.append( indice );
}
if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
datasetIndexList.append( layer->activeVectorDatasetAtTime( range ) );
for ( int groupIndex : allGroup )
{
if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
datasetIndexList.append( layer->datasetIndexAtTime( range, groupIndex ) );
}
}
else
{
if ( activeScalarGroup >= 0 )
datasetIndexList.append( layer->staticScalarDatasetIndex() );
if ( activeVectorGroup >= 0 && activeVectorGroup != activeScalarGroup )
datasetIndexList.append( layer->staticVectorDatasetIndex() );
for ( int groupIndex : allGroup )
{
if ( groupIndex != activeScalarGroup && groupIndex != activeVectorGroup )
{
if ( !layer->datasetGroupMetadata( groupIndex ).isTemporal() )
datasetIndexList.append( groupIndex );
}
}
}
double searchRadius = Qgis::DEFAULT_SEARCH_RADIUS_MM * renderContext.scaleFactor() * renderContext.mapToPixel().mapUnitsPerPixel();
double scalarDoubleValue = 0.0;
for ( const QgsMeshDatasetIndex &index : datasetIndexList )
{
if ( !index.isValid() )
continue;
const QgsMeshDatasetGroupMetadata &groupMeta = layer->datasetGroupMetadata( index );
QMap<QString, QString> derivedAttributes;
QMap<QString, QString> attribute;
if ( groupMeta.isScalar() )
{
const QgsMeshDatasetValue scalarValue = layer->datasetValueUncached( renderContext, index, *infoPoint, searchRadius ); //
scalarDoubleValue = scalarValue.scalar();
attribute.insert( "Scalar Value", std::isnan( scalarDoubleValue ) ? "no data" : QLocale().toString( scalarDoubleValue ) );
}
if ( groupMeta.isVector() )
{
const QgsMeshDatasetValue vectorValue = layer->datasetValueUncached( renderContext, index, *infoPoint, searchRadius ); //
const double vectorX = vectorValue.x();
const double vectorY = vectorValue.y();
if ( std::isnan( vectorX ) || std::isnan( vectorY ) )
{
attribute.insert( "Vector Value", "no data" );
}
else
{
attribute.insert( "Vector Magnitude", QLocale().toString( vectorValue.scalar() ) );
derivedAttributes.insert( "Vector x-component", QLocale().toString( vectorY ) );
derivedAttributes.insert( "Vector y-component", QLocale().toString( vectorX ) );
}
}
const QgsMeshDatasetMetadata &meta = layer->datasetMetadata( index );
if ( groupMeta.isTemporal() )
derivedAttributes.insert( "Time Step", layer->formatTime( meta.time() ) );
derivedAttributes.insert( "Source", groupMeta.uri() );
QString resultName = groupMeta.name();
QDomElement attributeElement = infoDocument.createElement( QStringLiteral( "Attribute" ) );
attributeElement.setAttribute( QStringLiteral( "name" ), resultName );
QString value;
if ( !QgsVariantUtils::isNull( scalarDoubleValue ) )
{
value = QString::number( scalarDoubleValue );
}
attributeElement.setAttribute( QStringLiteral( "value" ), value );
layerElement.appendChild( attributeElement );
if ( isTemporal )
{
QDomElement attributeElementTime = infoDocument.createElement( QStringLiteral( "Attribute" ) );
attributeElementTime.setAttribute( QStringLiteral( "name" ), "Time" );
if ( range.isInstant() )
{
value = range.begin().toString( dateFormat );
}
else
{
value = range.begin().toString( dateFormat ) + '/' + range.end().toString( dateFormat );
}
attributeElementTime.setAttribute( QStringLiteral( "value" ), value );
layerElement.appendChild( attributeElementTime );
}
}
return true;
}
bool QgsRenderer::featureInfoFromRasterLayer( QgsRasterLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const bool QgsRenderer::featureInfoFromRasterLayer( QgsRasterLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const
{ {
Q_UNUSED( version ) Q_UNUSED( version )
@ -2821,7 +2965,8 @@ namespace QgsWms
QByteArray QgsRenderer::convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc, const QgsCoordinateReferenceSystem &destCRS ) const QByteArray QgsRenderer::convertFeatureInfoToJson( const QList<QgsMapLayer *> &layers, const QDomDocument &doc, const QgsCoordinateReferenceSystem &destCRS ) const
{ {
json json { json json
{
{ "type", "FeatureCollection" }, { "type", "FeatureCollection" },
{ "features", json::array() }, { "features", json::array() },
}; };
@ -2971,7 +3116,8 @@ namespace QgsWms
} }
json["features"].push_back( json["features"].push_back(
{ { "type", "Feature" }, {
{ "type", "Feature" },
{ "id", layerName.toStdString() }, { "id", layerName.toStdString() },
{ "properties", properties } { "properties", properties }
} }
@ -3024,7 +3170,7 @@ namespace QgsWms
expressionContext.setFeature( *feat ); expressionContext.setFeature( *feat );
QgsEditFormConfig editConfig { layer ? layer->editFormConfig() : QgsEditFormConfig() }; QgsEditFormConfig editConfig { layer ? layer->editFormConfig() : QgsEditFormConfig() };
const bool honorFormConfig { layer && QgsServerProjectUtils::wmsFeatureInfoUseAttributeFormSettings( *mProject ) && editConfig.layout() == Qgis::AttributeFormLayout::DragAndDrop }; const bool honorFormConfig { layer &&QgsServerProjectUtils::wmsFeatureInfoUseAttributeFormSettings( *mProject ) &&editConfig.layout() == Qgis::AttributeFormLayout::DragAndDrop };
// always add bounding box info if feature contains geometry and has been // always add bounding box info if feature contains geometry and has been
// explicitly configured in the project // explicitly configured in the project
@ -3064,7 +3210,8 @@ namespace QgsWms
// find if an attribute is in any form tab // find if an attribute is in any form tab
std::function<bool( const QString &, const QgsAttributeEditorElement * )> findAttributeInTree; std::function<bool( const QString &, const QgsAttributeEditorElement * )> findAttributeInTree;
findAttributeInTree = [&findAttributeInTree, &layer]( const QString &attributeName, const QgsAttributeEditorElement *group ) -> bool { findAttributeInTree = [&findAttributeInTree, &layer]( const QString & attributeName, const QgsAttributeEditorElement * group ) -> bool
{
const QgsAttributeEditorContainer *container = dynamic_cast<const QgsAttributeEditorContainer *>( group ); const QgsAttributeEditorContainer *container = dynamic_cast<const QgsAttributeEditorContainer *>( group );
if ( container ) if ( container )
{ {

View File

@ -40,6 +40,7 @@ class QgsMapRendererTask;
class QgsMapSettings; class QgsMapSettings;
class QgsPointXY; class QgsPointXY;
class QgsRasterLayer; class QgsRasterLayer;
class QgsMeshLayer;
class QgsRectangle; class QgsRectangle;
class QgsRenderContext; class QgsRenderContext;
class QgsVectorLayer; class QgsVectorLayer;
@ -278,6 +279,7 @@ namespace QgsWms
void writeVectorLayerAttribute( int attributeIndex, QgsVectorLayer *layer, const QgsFields &fields, QgsAttributes &featureAttributes, QDomDocument &doc, QDomElement &featureElem, QgsRenderContext &renderContext, QStringList *attributes = nullptr ) const; void writeVectorLayerAttribute( int attributeIndex, QgsVectorLayer *layer, const QgsFields &fields, QgsAttributes &featureAttributes, QDomDocument &doc, QDomElement &featureElem, QgsRenderContext &renderContext, QStringList *attributes = nullptr ) const;
//! Appends feature info xml for the layer to the layer element of the dom document //! Appends feature info xml for the layer to the layer element of the dom document
bool featureInfoFromMeshLayer( QgsMeshLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const;
bool featureInfoFromRasterLayer( QgsRasterLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const; bool featureInfoFromRasterLayer( QgsRasterLayer *layer, const QgsMapSettings &mapSettings, const QgsPointXY *infoPoint, const QgsRenderContext &renderContext, QDomDocument &infoDocument, QDomElement &layerElement, const QString &version ) const;
//! Record which symbols would be used if the map was in the current configuration of renderer. This is useful for content-based legend //! Record which symbols would be used if the map was in the current configuration of renderer. This is useful for content-based legend