Skip to content

Commit

Permalink
WMS: Better logic to pick the legend URL
Browse files Browse the repository at this point in the history
QGIS had two problems:
1) It was using the specified legend URL only if its mime type was matching
   the layer's mime type. There is no reason for that.
2) When QGIS was using the default layer (empty string), it was not even
   trying to find out in what style to pick the legend URL.
  • Loading branch information
Patrick Valsecchi committed May 29, 2016
1 parent e79a327 commit 69bed21
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/providers/wms/qgswmscapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ void QgsWmsCapabilities::parseLayer( QDomElement const & e, QgsWmsLayerProperty&

for ( int i = 0; i < layerProperty.style.size(); ++i )
{
if ( layerProperty.style[i].name == styleProperty.name )
if ( layerProperty.style.at( i ).name == styleProperty.name )
{
// override inherited parent's style if it has the same name
// according to the WMS spec, it should not happen, but Mapserver
Expand Down
68 changes: 52 additions & 16 deletions src/providers/wms/qgswmsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,36 @@ QString QgsWmsProvider::getTileUrl() const
}
}

static bool isValidLegend( const QgsWmsLegendUrlProperty &l )
{
return l.format.startsWith( "image/" );
}

/**
* Picks a usable legend URL for a given style.
*/
static QString pickLegend( const QgsWmsStyleProperty &s )
{
QString url;
for ( int k = 0; k < s.legendUrl.size() && url.isEmpty(); k++ )
{
const QgsWmsLegendUrlProperty &l = s.legendUrl[k];
if ( isValidLegend( l ) )
{
url = l.onlineResource.xlinkHref;
}
}
return url;
}

static const QgsWmsStyleProperty *searchStyle( const QVector<QgsWmsStyleProperty>& styles, const QString& name )
{
Q_FOREACH ( const QgsWmsStyleProperty &s, styles )
if ( s.name == name )
return &s;
return nullptr;
}

QString QgsWmsProvider::getLegendGraphicUrl() const
{
QString url;
Expand All @@ -223,25 +253,31 @@ QString QgsWmsProvider::getLegendGraphicUrl() const
{
const QgsWmsLayerProperty &l = mCaps.mLayersSupported[i];

if ( l.name != mSettings.mActiveSubLayers[0] )
continue;

for ( int j = 0; j < l.style.size() && url.isEmpty(); j++ )
if ( l.name == mSettings.mActiveSubLayers[0] )
{
const QgsWmsStyleProperty &s = l.style[j];

if ( s.name != mSettings.mActiveSubStyles[0] )
continue;

for ( int k = 0; k < s.legendUrl.size() && url.isEmpty(); k++ )
if ( !mSettings.mActiveSubStyles[0].isEmpty() && mSettings.mActiveSubStyles[0] != "default" )
{
const QgsWmsLegendUrlProperty &l = s.legendUrl[k];

if ( l.format != mSettings.mImageMimeType )
continue;

url = l.onlineResource.xlinkHref;
const QgsWmsStyleProperty *s = searchStyle( l.style, mSettings.mActiveSubStyles[0] );
if ( s )
url = pickLegend( *s );
}
else
{
// QGIS wants the default style, but GetCapabilities doesn't give us a
// way to know what is the default style. So we look for the onlineResource
// only if there is a single style available or if there is a style called "default".
if ( l.style.size() == 1 )
{
url = pickLegend( l.style[0] );
}
else
{
const QgsWmsStyleProperty *s = searchStyle( l.style, "default" );
if ( s )
url = pickLegend( *s );
}
}
break;
}
}

Expand Down
4 changes: 4 additions & 0 deletions tests/src/providers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ ADD_QGIS_TEST(wmscapabilititestest
testqgswmscapabilities.cpp)
TARGET_LINK_LIBRARIES(qgis_wmscapabilititestest wmsprovider_a)

ADD_QGIS_TEST(wmsprovidertest
testqgswmsprovider.cpp)
TARGET_LINK_LIBRARIES(qgis_wmsprovidertest wmsprovider_a)

#############################################################
# WCS public servers test:
# No need to test on all platforms
Expand Down
69 changes: 69 additions & 0 deletions tests/src/providers/testqgswmsprovider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <QFile>
#include <QObject>
#include <QtTest/QtTest>
#include <qgswmsprovider.h>
#include <qgsapplication.h>

/** \ingroup UnitTests
* This is a unit test for the WMS provider.
*/
class TestQgsWmsProvider: public QObject
{
Q_OBJECT
private slots:

void initTestCase()
{
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init();
QgsApplication::initQgis();

QFile file( QString( TEST_DATA_DIR ) + "/provider/GetCapabilities.xml" );
QVERIFY( file.open( QIODevice::ReadOnly | QIODevice::Text ) );
const QByteArray content = file.readAll();
QVERIFY( content.size() > 0 );
const QgsWmsParserSettings config;

mCapabilities = new QgsWmsCapabilities();
QVERIFY( mCapabilities->parseResponse( content, config ) );
}

//runs after all tests
void cleanupTestCase()
{
delete mCapabilities;
QgsApplication::exitQgis();
}

void legendGraphicsWithStyle()
{
QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=fb_style&format=image/jpg", mCapabilities );
QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/fb.png?" ) );
}

void legendGraphicsWithSecondStyle()
{
QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=yt_style&format=image/jpg", mCapabilities );
QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/yt.png?" ) );
}

void legendGraphicsWithoutStyleWithDefault()
{
QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=buildings&styles=&format=image/jpg", mCapabilities );
//only one style, can guess default => use it
QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/buildings.png?" ) );
}

void legendGraphicsWithoutStyleWithoutDefault()
{
QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=&format=image/jpg", mCapabilities );
//two style, cannot guess default => use the WMS GetLegendGraphics
QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://localhost:8380/mapserv?" ) );
}

private:
QgsWmsCapabilities* mCapabilities;
};

QTEST_MAIN( TestQgsWmsProvider )
#include "testqgswmsprovider.moc"

3 comments on commit 69bed21

@rduivenvoorde
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pvalsecc Hi Patrick, can you please have a look at this issue at the user mailing list (and my analysis of it):

https://lists.osgeo.org/pipermail/qgis-user/2016-September/037640.html

It looks like for a (raster) layer in that service, without an actual legendgraphic, this algorithm tries to pick the parent node style + legend. Which in this case is plain wrong.

Any idea how to fix this?

Regards,

Richard Duivenvoorde (richard@qgis.org)

@pvalsecc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rduivenvoorde,

IMHO, there is nothing to fix in QGIS, here. If a layer defines no style, it's wanted that it inherits the parent layer's style. Have a look at chapter 7.2.4.6.5 of the WMS implementation specification:

Style declarations are inherited by child Layers. A child shall not redefine a Style with the same Name as one
inherited from a parent. A child may define a new Style with a new Name that is not available for the parent Layer. 

This PR is just a workaround for MapServer that is defining a style named default for both the parent and the child layers. This was making QGIS show 2 default styles for every child layers. Style inheritance was already there before. Now at has just a saner behavior in regard to non-standard compliant servers.

I'll let you forward that to the user ML. I was not subscribed to it and therefore cannot reply directly to the thread.

Thanks.

@rduivenvoorde
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pvalsecc I think you are right in your analysis, see also:

https://lists.osgeo.org/pipermail/qgis-user/2016-September/037674.html

Thanks for your response

Please sign in to comment.