Skip to content

Commit

Permalink
wms convert color to value following mapTiler encoding scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
vcloarec authored and nyalldawson committed Jan 8, 2022
1 parent 50748cd commit 1ab5432
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 43 deletions.
7 changes: 6 additions & 1 deletion src/providers/wms/qgswmscapabilities.cpp
Expand Up @@ -55,6 +55,11 @@ bool QgsWmsSettings::parseUri( const QString &uriString )
mAuth.mReferer = uri.param( QStringLiteral( "referer" ) );
mXyz = false; // assume WMS / WMTS

if ( uri.hasParam( QStringLiteral( "encodingScheme" ) ) )
{
mEncodingScheme = uri.param( QStringLiteral( "encodingScheme" ) );
}

if ( uri.param( QStringLiteral( "type" ) ) == QLatin1String( "xyz" ) ||
uri.param( QStringLiteral( "type" ) ) == QLatin1String( "mbtiles" ) )
{
Expand All @@ -69,7 +74,7 @@ bool QgsWmsSettings::parseUri( const QString &uriString )
mBaseUrl = mHttpUri;
mIgnoreGetMapUrl = false;
mIgnoreGetFeatureInfoUrl = false;
mSmoothPixmapTransform = true;
mSmoothPixmapTransform = mEncodingScheme.isEmpty();
mDpiMode = DpiNone; // does not matter what we set here
mActiveSubLayers = QStringList( QStringLiteral( "xyz" ) ); // just a placeholder to have one sub-layer
mActiveSubStyles = QStringList( QStringLiteral( "xyz" ) ); // just a placeholder to have one sub-style
Expand Down
2 changes: 2 additions & 0 deletions src/providers/wms/qgswmscapabilities.h
Expand Up @@ -858,6 +858,8 @@ class QgsWmsSettings

bool mEnableContextualLegend;

QString mEncodingScheme;

friend class QgsWmsProvider;
};

Expand Down
82 changes: 79 additions & 3 deletions src/providers/wms/qgswmsprovider.cpp
Expand Up @@ -221,6 +221,9 @@ QgsWmsProvider::QgsWmsProvider( QString const &uri, const ProviderOptions &optio
// 2) http://xxx.xxx.xx/yyy/yyy?
// 3) http://xxx.xxx.xx/yyy/yyy?zzz=www


mConverter.reset( QgsWmsConverter::createConverter( mSettings.mEncodingScheme ) );

mValid = true;
QgsDebugMsgLevel( QStringLiteral( "exiting constructor." ), 4 );
}
Expand Down Expand Up @@ -884,7 +887,7 @@ QImage *QgsWmsProvider::draw( QgsRectangle const &viewExtent, int pixelWidth, in
r.rect.width() / cr,
r.rect.height() / cr );
// if image size is "close enough" to destination size, don't smooth it out. Instead try for pixel-perfect placement!
bool disableSmoothing = ( qgsDoubleNear( dst.width(), tm->tileWidth, 2 ) && qgsDoubleNear( dst.height(), tm->tileHeight, 2 ) );
bool disableSmoothing = ( qgsDoubleNear( dst.width(), tm->tileWidth, 2 ) && qgsDoubleNear( dst.height(), tm->tileHeight, 2 ) ) || mConverter ;
tileImages << TileImage( dst, localImage, !disableSmoothing );
}
else
Expand Down Expand Up @@ -1018,7 +1021,25 @@ bool QgsWmsProvider::readBlock( int bandNo, QgsRectangle const &viewExtent, int
if ( ptr )
{
// If image is too large, ptr can be NULL
memcpy( block, ptr, myExpectedSize );
if ( mConverter && ( image->format() == QImage::Format_ARGB32 || image->format() == QImage::Format_RGB32 ) )
{
std::vector<float> data;
data.resize( myImageSize );
const QRgb *inputPtr = reinterpret_cast<const QRgb *>( image->constBits() );
float *outputPtr = data.data();
for ( int i = 0; i < pixelWidth * pixelHeight; ++i )
{
mConverter->convert( *inputPtr, outputPtr );
inputPtr++;
outputPtr++;
}

memcpy( block, data.data(), myExpectedSize );

}
else
memcpy( block, ptr, myExpectedSize );

return true;
}
else
Expand Down Expand Up @@ -1600,7 +1621,10 @@ Qgis::DataType QgsWmsProvider::dataType( int bandNo ) const
Qgis::DataType QgsWmsProvider::sourceDataType( int bandNo ) const
{
Q_UNUSED( bandNo )
return Qgis::DataType::ARGB32;
if ( mConverter )
return mConverter->dataType();
else
return Qgis::DataType::ARGB32;
}

int QgsWmsProvider::bandCount() const
Expand Down Expand Up @@ -3673,6 +3697,22 @@ QgsLayerMetadata QgsWmsProvider::layerMetadata() const
return mLayerMetadata;
}

QgsRasterBandStats QgsWmsProvider::bandStatistics( int, int, const QgsRectangle &, int, QgsRasterBlockFeedback * )
{
if ( mConverter )
return mConverter->statistics();
else
return QgsRasterBandStats();
}

QgsRasterHistogram QgsWmsProvider::histogram( int, int, double, double, const QgsRectangle &, int, bool, QgsRasterBlockFeedback * )
{
if ( mConverter )
return mConverter->histogram();
else
return QgsRasterHistogram();
}

QVector<QgsWmsSupportedFormat> QgsWmsProvider::supportedFormats()
{
QVector<QgsWmsSupportedFormat> formats;
Expand Down Expand Up @@ -4787,3 +4827,39 @@ QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
return new QgsWmsProviderMetadata();
}
#endif

Qgis::DataType QgsWmsConverter::dataType() const
{
return Qgis::DataType::Float32;
}

QgsWmsConverter *QgsWmsConverter::createConverter( const QString &key )
{
if ( key == QgsWmsConverterMapTilerTerrainRGB::encodingSchemeKey() )
return new QgsWmsConverterMapTilerTerrainRGB();

return nullptr;
}

void QgsWmsConverterMapTilerTerrainRGB::convert( const QRgb &color, float *converted ) const
{
int R = qRed( color );
int G = qGreen( color );
int B = qBlue( color );

*converted = -10000 + ( ( R * 256 * 256 + G * 256 + B ) ) * 0.1;
}

QgsRasterBandStats QgsWmsConverterMapTilerTerrainRGB::statistics() const
{
QgsRasterBandStats stat;
stat.minimumValue = 0;
stat.maximumValue = 9000;
stat.statsGathered = QgsRasterBandStats::Min | QgsRasterBandStats::Max;
return stat;
}

QgsRasterHistogram QgsWmsConverterMapTilerTerrainRGB::histogram() const
{
return QgsRasterHistogram();
}
52 changes: 52 additions & 0 deletions src/providers/wms/qgswmsprovider.h
Expand Up @@ -99,6 +99,41 @@ class QgsCachedImageFetcher: public QgsImageFetcher
}
};

//! Abstract class to convert color to float value following encoding scheme
class QgsWmsConverter
{
public:
virtual ~QgsWmsConverter() = default;

//! Convert the \a color to a value pointed by float
virtual void convert( const QRgb &color, float *converted ) const = 0;

//! Returns the output datatype of this converter
virtual Qgis::DataType dataType() const;

//! Returns statistics related to converted values
virtual QgsRasterBandStats statistics() const = 0;

//! Returns the histogram related to converted values
virtual QgsRasterHistogram histogram() const = 0;

//! Creates a converter instance corresponding to the \a key
static QgsWmsConverter *createConverter( const QString &key );
};


//! Abstract class to convert color to float value following the mapTiler terrain RGB encoding scheme
class QgsWmsConverterMapTilerTerrainRGB : public QgsWmsConverter
{
public:
void convert( const QRgb &color, float *converted ) const override;

QgsRasterBandStats statistics() const override;
QgsRasterHistogram histogram() const override;

static QString displayName() {return QObject::tr( "MapTiler Terrain RGB" );}
static QString encodingSchemeKey() {return QStringLiteral( "maptilerterrain" );}
};

/**
*
Expand Down Expand Up @@ -219,6 +254,21 @@ class QgsWmsProvider final: public QgsRasterDataProvider
QList< double > nativeResolutions() const override;
QgsLayerMetadata layerMetadata() const override;

//! Statitics could be available if the provider has a converter from colors to other value type, the returned statistics depend on the converter
QgsRasterBandStats bandStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr ) override;

virtual QgsRasterHistogram histogram( int bandNo,
int binCount = 0,
double minimum = std::numeric_limits<double>::quiet_NaN(),
double maximum = std::numeric_limits<double>::quiet_NaN(),
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0,
bool includeOutOfRange = false,
QgsRasterBlockFeedback *feedback = nullptr ) override;

static QVector<QgsWmsSupportedFormat> supportedFormats();

static void showMessageBox( const QString &title, const QString &text );
Expand Down Expand Up @@ -492,6 +542,8 @@ class QgsWmsProvider final: public QgsRasterDataProvider

QList< double > mNativeResolutions;

std::unique_ptr<QgsWmsConverter> mConverter;

friend class TestQgsWmsProvider;

};
Expand Down
59 changes: 59 additions & 0 deletions src/providers/wms/qgswmssourceselect.cpp
Expand Up @@ -143,6 +143,9 @@ QgsWMSSourceSelect::QgsWMSSourceSelect( QWidget *parent, Qt::WindowFlags fl, Qgs
tabLayers->layout()->removeWidget( gbCRS );
}

mEncodingSchemeWidget = new QgsWMSEncodingSchemeWidget( this );
mEncodingSchemeLayout->addWidget( mEncodingSchemeWidget );

clear();

// set up the WMS connections we already know about
Expand Down Expand Up @@ -284,6 +287,8 @@ void QgsWMSSourceSelect::clear()
}

mFeatureCount->setEnabled( false );

mEncodingSchemeWidget->setEncodingScheme( QString() );
}

bool QgsWMSSourceSelect::populateLayerList( const QgsWmsCapabilities &capabilities )
Expand Down Expand Up @@ -599,6 +604,9 @@ void QgsWMSSourceSelect::addButtonClicked()
uri.setParam( QStringLiteral( "featureCount" ), mFeatureCount->text() );
}

if ( tabTilesets->isEnabled() && !mEncodingSchemeWidget->encodingScheme().isEmpty() )
uri.setParam( QStringLiteral( "encodingScheme" ), mEncodingSchemeWidget->encodingScheme() );

uri.setParam( QStringLiteral( "contextualWMSLegend" ), mContextualLegendCheckbox->isChecked() ? "1" : "0" );

emit addRasterLayer( uri.encodedUri(),
Expand Down Expand Up @@ -1308,3 +1316,54 @@ void QgsWMSSourceSelect::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "working_with_ogc/ogc_client_support.html" ) );
}

QgsWMSEncodingSchemeWidget::QgsWMSEncodingSchemeWidget( QWidget *parent ): QWidget( parent )
{
QHBoxLayout *lay = new QHBoxLayout( this );
lay->setContentsMargins( 0, 0, 0, 0 );
setLayout( lay );
mCheckBox = new QCheckBox( this );
mCheckBox->setText( tr( "Convert to single band raster" ) );
mCheckBox->setChecked( false );
layout()->addWidget( mCheckBox );

mCombo = new QComboBox( this );
mCombo->setEnabled( false );
layout()->addWidget( mCombo );

connect( mCheckBox, &QAbstractButton::toggled, mCombo, &QComboBox::setEnabled );

populateEncodingScheme();
}

void QgsWMSEncodingSchemeWidget::populateEncodingScheme()
{
mCombo->addItem( QgsWmsConverterMapTilerTerrainRGB::displayName(), QgsWmsConverterMapTilerTerrainRGB::encodingSchemeKey() );
}

void QgsWMSEncodingSchemeWidget::setEncodingScheme( const QString &encodingSchemeKey )
{
bool checked = false;
if ( ! encodingSchemeKey.isEmpty() )
{
int index = mCombo->findData( encodingSchemeKey );
if ( index != -1 )
{
checked = true;
mCombo->setCurrentIndex( index );
}
else
checked = false;
}

mCheckBox->setChecked( checked );
mCombo->setEnabled( checked );
}

QString QgsWMSEncodingSchemeWidget::encodingScheme() const
{
if ( mCheckBox->isChecked() )
return mCombo->currentData().toString();

return QString();
}
28 changes: 27 additions & 1 deletion src/providers/wms/qgswmssourceselect.h
Expand Up @@ -33,6 +33,7 @@ class QgsTreeWidgetItem;
class QDomDocument;
class QDomElement;
class QgsWmsCapabilities;
class QgsWMSEncodingSchemeWidget;

/*!
* \brief Dialog to create connections and add layers from WMS, etc.
Expand Down Expand Up @@ -128,7 +129,6 @@ class QgsWMSSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsWM
//! Map mime types to supported formats
QMap<QString, int> mMimeMap;


// Clear layers list, crs, encodings ...
void clear();

Expand Down Expand Up @@ -193,6 +193,8 @@ class QgsWMSSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsWM
// save the current status of the layer true
QMap<QTreeWidgetItem *, bool> mTreeInitialExpand = QMap<QTreeWidgetItem *, bool>();

QgsWMSEncodingSchemeWidget *mEncodingSchemeWidget = nullptr;

private slots:
void lstTilesets_itemClicked( QTableWidgetItem *item );
void mLayerUpButton_clicked();
Expand All @@ -201,5 +203,29 @@ class QgsWMSSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsWM
void showHelp();
};

/*!
* \brief Widget that embeds a check box and a combo box to select an encoding scheme to convert image to a single band raster
*/
class QgsWMSEncodingSchemeWidget : public QWidget
{
public:
//! Contructor
QgsWMSEncodingSchemeWidget( QWidget *parent = nullptr );

/**
* Sets the current encoding scheme from its key \a encodingSchemeKey,
* leading to checked the check box if the encoding scheme exists or unchecked if the key is empty or the scheme does not exist
*/
void setEncodingScheme( const QString &encodingSchemeKey );

//! Returns the key of the current selected scheme, returns empty string if the check box is unchecked
QString encodingScheme() const;

private:
QCheckBox *mCheckBox = nullptr;
QComboBox *mCombo = nullptr;

void populateEncodingScheme();
};

#endif // QGSWMSSOURCESELECT_H
4 changes: 4 additions & 0 deletions src/providers/wms/qgsxyzconnection.cpp
Expand Up @@ -38,6 +38,8 @@ QString QgsXyzConnection::encodedUri() const
uri.setParam( QStringLiteral( "referer" ), referer );
if ( tilePixelRatio != 0 )
uri.setParam( QStringLiteral( "tilePixelRatio" ), QString::number( tilePixelRatio ) );
if ( !encodingScheme.isEmpty() )
uri.setParam( QStringLiteral( "encodingScheme" ), encodingScheme );
return uri.encodedUri();
}

Expand Down Expand Up @@ -94,6 +96,7 @@ QgsXyzConnection QgsXyzConnectionUtils::connection( const QString &name )
conn.referer = settings.value( QStringLiteral( "referer" ) ).toString();
conn.tilePixelRatio = settings.value( QStringLiteral( "tilePixelRatio" ), 0 ).toDouble();
conn.hidden = settings.value( QStringLiteral( "hidden" ) ).toBool();
conn.encodingScheme = settings.value( QStringLiteral( "encodingScheme" ), conn.encodingScheme ).toString();
return conn;
}

Expand Down Expand Up @@ -136,6 +139,7 @@ void QgsXyzConnectionUtils::addConnection( const QgsXyzConnection &conn )
settings.setValue( QStringLiteral( "password" ), conn.password );
settings.setValue( QStringLiteral( "referer" ), conn.referer );
settings.setValue( QStringLiteral( "tilePixelRatio" ), conn.tilePixelRatio );
settings.setValue( QStringLiteral( "encodingScheme" ), conn.encodingScheme );
if ( addHiddenProperty )
{
settings.setValue( QStringLiteral( "hidden" ), false );
Expand Down
3 changes: 3 additions & 0 deletions src/providers/wms/qgsxyzconnection.h
Expand Up @@ -36,6 +36,9 @@ struct QgsXyzConnection
double tilePixelRatio = 0;
bool hidden = false;

// Encoding scheme key related to the converter from color to value, empty if none
QString encodingScheme;

QString encodedUri() const;
};

Expand Down

0 comments on commit 1ab5432

Please sign in to comment.