293 changes: 292 additions & 1 deletion src/mapserver/qgshttprequesthandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ void QgsHttpRequestHandler::sendGetMapResponse( const QString& service, QImage*

if ( png8Bit )
{
QImage palettedImg = img->convertToFormat( QImage::Format_Indexed8, Qt::ColorOnly | Qt::ThresholdDither |
QVector<QRgb> colorTable;
medianCut( colorTable, 256, *img );
QImage palettedImg = img->convertToFormat( QImage::Format_Indexed8, colorTable, Qt::ColorOnly | Qt::ThresholdDither |
Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection );
palettedImg.save( &buffer, "PNG", -1 );
}
Expand Down Expand Up @@ -469,3 +471,292 @@ QString QgsHttpRequestHandler::readPostBody() const
}
return inputString;
}

void QgsHttpRequestHandler::medianCut( QVector<QRgb>& colorTable, int nColors, const QImage& inputImage )
{
QHash<QRgb, int> inputColors;
imageColors( inputColors, inputImage );

if ( inputColors.size() <= nColors ) //all the colors in the image can be mapped to one palette color
{
colorTable.resize( inputColors.size() );
int index = 0;
QHash<QRgb, int>::const_iterator inputColorIt = inputColors.constBegin();
for ( ; inputColorIt != inputColors.constEnd(); ++inputColorIt )
{
colorTable[index] = inputColorIt.key();
++index;
}
return;
}

//create first box
QgsColorBox firstBox; //QList< QPair<QRgb, int> >
int firstBoxPixelSum = 0;
QHash<QRgb, int>::const_iterator inputColorIt = inputColors.constBegin();
for ( ; inputColorIt != inputColors.constEnd(); ++inputColorIt )
{
firstBox.push_back( qMakePair( inputColorIt.key(), inputColorIt.value() ) );
firstBoxPixelSum += inputColorIt.value();
}

QgsColorBoxMap colorBoxMap; //QMultiMap< int, ColorBox >
colorBoxMap.insert( firstBoxPixelSum, firstBox );
QMap<int, QgsColorBox>::iterator colorBoxMapIt = colorBoxMap.end();

//split boxes until number of boxes == nColors or all the boxes have color count 1
bool allColorsMapped = false;
while ( colorBoxMap.size() < nColors )
{
//start at the end of colorBoxMap and pick the first entry with number of colors < 1
colorBoxMapIt = colorBoxMap.end();
while ( true )
{
--colorBoxMapIt;
if ( colorBoxMapIt.value().size() > 1 )
{
splitColorBox( colorBoxMapIt.value(), colorBoxMap, colorBoxMapIt );
break;
}
if ( colorBoxMapIt == colorBoxMap.begin() )
{
allColorsMapped = true;
break;
}
}

if ( allColorsMapped )
{
break;
}
else
{
continue;
}
}

//get representative colors for the boxes
int index = 0;
colorTable.resize( colorBoxMap.size() );
QgsColorBoxMap::const_iterator colorBoxIt = colorBoxMap.constBegin();
for ( ; colorBoxIt != colorBoxMap.constEnd(); ++colorBoxIt )
{
colorTable[index] = boxColor( colorBoxIt.value(), colorBoxIt.key() );
++index;
}
}

void QgsHttpRequestHandler::imageColors( QHash<QRgb, int>& colors, const QImage& image )
{
colors.clear();
int width = image.width();
int height = image.height();

QRgb currentColor;
QHash<QRgb, int>::iterator colorIt;
for ( int i = 0; i < height; ++i )
{
for ( int j = 0; j < width; ++j )
{
currentColor = image.pixel( j, i );
colorIt = colors.find( currentColor );
if ( colorIt == colors.end() )
{
colors.insert( currentColor, 1 );
}
else
{
colorIt.value()++;
}
}
}
}

void QgsHttpRequestHandler::splitColorBox( QgsColorBox& colorBox, QgsColorBoxMap& colorBoxMap,
QMap<int, QgsColorBox>::iterator colorBoxMapIt )
{

if ( colorBox.size() < 2 )
{
return; //need at least two colors for a split
}

//a,r,g,b ranges
int redRange = 0;
int greenRange = 0;
int blueRange = 0;
int alphaRange = 0;

if ( !minMaxRange( colorBox, redRange, greenRange, blueRange, alphaRange ) )
{
return;
}

//sort color box for a/r/g/b
if ( redRange >= greenRange && redRange >= blueRange && redRange >= alphaRange )
{
qSort( colorBox.begin(), colorBox.end(), redCompare );
}
else if ( greenRange >= redRange && greenRange >= blueRange && greenRange >= alphaRange )
{
qSort( colorBox.begin(), colorBox.end(), greenCompare );
}
else if ( blueRange >= redRange && blueRange >= greenRange && blueRange >= alphaRange )
{
qSort( colorBox.begin(), colorBox.end(), blueCompare );
}
else
{
qSort( colorBox.begin(), colorBox.end(), alphaCompare );
}

//get median
double halfSum = colorBoxMapIt.key() / 2.0;
int currentSum = 0;
int currentListIndex = 0;

QgsColorBox::iterator colorBoxIt = colorBox.begin();
for ( ; colorBoxIt != colorBox.end(); ++colorBoxIt )
{
currentSum += colorBoxIt->second;
if ( currentSum >= halfSum )
{
break;
}
++currentListIndex;
}

if ( currentListIndex > ( colorBox.size() - 2 ) ) //if the median is contained in the last color, split one item before that
{
--currentListIndex;
currentSum -= colorBoxIt->second;
}
else
{
++colorBoxIt; //the iterator needs to point behind the last item to remove
}

//do split: replace old color box, insert new one
QgsColorBox newColorBox1 = colorBox.mid( 0, currentListIndex + 1 );
colorBoxMap.insert( currentSum, newColorBox1 );

colorBox.erase( colorBox.begin(), colorBoxIt );
QgsColorBox newColorBox2 = colorBox;
colorBoxMap.erase( colorBoxMapIt );
colorBoxMap.insert( halfSum * 2.0 - currentSum, newColorBox2 );
}

bool QgsHttpRequestHandler::minMaxRange( const QgsColorBox& colorBox, int& redRange, int& greenRange, int& blueRange, int& alphaRange )
{
if ( colorBox.size() < 1 )
{
return false;
}

int rMin = INT_MAX;
int gMin = INT_MAX;
int bMin = INT_MAX;
int aMin = INT_MAX;
int rMax = INT_MIN;
int gMax = INT_MIN;
int bMax = INT_MIN;
int aMax = INT_MIN;

int currentRed = 0; int currentGreen = 0; int currentBlue = 0; int currentAlpha = 0;

QgsColorBox::const_iterator colorBoxIt = colorBox.constBegin();
for ( ; colorBoxIt != colorBox.constEnd(); ++colorBoxIt )
{
currentRed = qRed( colorBoxIt->first );
if ( currentRed > rMax )
{
rMax = currentRed;
}
if ( currentRed < rMin )
{
rMin = currentRed;
}

currentGreen = qGreen( colorBoxIt->first );
if ( currentGreen > gMax )
{
gMax = currentGreen;
}
if ( currentGreen < gMin )
{
gMin = currentGreen;
}

currentBlue = qBlue( colorBoxIt->first );
if ( currentBlue > bMax )
{
bMax = currentBlue;
}
if ( currentBlue < bMin )
{
bMin = currentBlue;
}

currentAlpha = qAlpha( colorBoxIt->first );
if ( currentAlpha > aMax )
{
aMax = currentAlpha;
}
if ( currentAlpha < aMin )
{
aMin = currentAlpha;
}
}

redRange = rMax - rMin;
greenRange = gMax - gMin;
blueRange = bMax - bMin;
alphaRange = aMax - aMin;
return true;
}

bool QgsHttpRequestHandler::redCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 )
{
return qRed( c1.first ) < qRed( c2.first );
}

bool QgsHttpRequestHandler::greenCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 )
{
return qGreen( c1.first ) < qGreen( c2.first );
}

bool QgsHttpRequestHandler::blueCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 )
{
return qBlue( c1.first ) < qBlue( c2.first );
}

bool QgsHttpRequestHandler::alphaCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 )
{
return qAlpha( c1.first ) < qAlpha( c2.first );
}

QRgb QgsHttpRequestHandler::boxColor( const QgsColorBox& box, int boxPixels )
{
double avRed = 0;
double avGreen = 0;
double avBlue = 0;
double avAlpha = 0;
QRgb currentColor;
int currentPixel;

double weight;

QgsColorBox::const_iterator colorBoxIt = box.constBegin();
for ( ; colorBoxIt != box.constEnd(); ++colorBoxIt )
{
currentColor = colorBoxIt->first;
currentPixel = colorBoxIt->second;
weight = ( double )currentPixel / boxPixels;
avRed += ( qRed( currentColor ) * weight );
avGreen += ( qGreen( currentColor ) * weight );
avBlue += ( qBlue( currentColor ) * weight );
avAlpha += ( qAlpha( currentColor ) * weight );
}

return qRgba( avRed, avGreen, avBlue, avAlpha );
}
18 changes: 18 additions & 0 deletions src/mapserver/qgshttprequesthandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
#define QGSHTTPREQUESTHANDLER_H

#include "qgsrequesthandler.h"
#include <QColor>
#include <QPair>

typedef QList< QPair<QRgb, int> > QgsColorBox; //Color / number of pixels
typedef QMultiMap< int, QgsColorBox > QgsColorBoxMap; // sum of pixels / color box

/**Base class for request handler using HTTP.
It provides a method to send data to the client*/
Expand Down Expand Up @@ -47,6 +52,19 @@ class QgsHttpRequestHandler: public QgsRequestHandler
void requestStringToParameterMap( const QString& request, QMap<QString, QString>& parameters );
/**Read CONTENT_LENGTH characters from stdin*/
QString readPostBody() const;

private:
static void medianCut( QVector<QRgb>& colorTable, int nColors, const QImage& inputImage );
static void imageColors( QHash<QRgb, int>& colors, const QImage& image );
static void splitColorBox( QgsColorBox& colorBox, QgsColorBoxMap& colorBoxMap,
QMap<int, QgsColorBox>::iterator colorBoxMapIt );
static bool minMaxRange( const QgsColorBox& colorBox, int& redRange, int& greenRange, int& blueRange, int& alphaRange );
static bool redCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 );
static bool greenCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 );
static bool blueCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 );
static bool alphaCompare( const QPair<QRgb, int>& c1, const QPair<QRgb, int>& c2 );
/**Calculates a representative color for a box (pixel weighted average)*/
static QRgb boxColor( const QgsColorBox& box, int boxPixels );
};

#endif
17 changes: 10 additions & 7 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,11 +1448,6 @@ QString QgsGdalProvider::buildPyramids( QList<QgsRasterPyramid> const & theRaste
QgsGdalProgress myProg;
myProg.type = ProgressPyramids;
myProg.provider = this;
// Observed problem: if a *.rrd file exists and GDALBuildOverviews() is called,
// the *.rrd is deleted and no overviews are created, if GDALBuildOverviews()
// is called next time, it crashes somewhere in GDAL:
// https://trac.osgeo.org/gdal/ticket/4831
// Crash can be avoided if dataset is reopened
myError = GDALBuildOverviews( mGdalBaseDataset, theMethod,
myOverviewLevelsVector.size(), myOverviewLevelsVector.data(),
0, NULL,
Expand Down Expand Up @@ -1492,8 +1487,16 @@ QString QgsGdalProvider::buildPyramids( QList<QgsRasterPyramid> const & theRaste

QgsDebugMsg( "Pyramid overviews built" );

// For now always reopen to avoid crash described above
if ( true || theFormat == PyramidsInternal )
// Observed problem: if a *.rrd file exists and GDALBuildOverviews() is called,
// the *.rrd is deleted and no overviews are created, if GDALBuildOverviews()
// is called next time, it crashes somewhere in GDAL:
// https://trac.osgeo.org/gdal/ticket/4831
// Crash can be avoided if dataset is reopened, fixed in GDAL 1.9.2
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1920
if ( theFormat == PyramidsInternal )
#else
if ( true ) // GDAL #4831 fix
#endif
{
QgsDebugMsg( "Reopening dataset ..." );
//close the gdal dataset and reopen it in read only mode
Expand Down
12 changes: 11 additions & 1 deletion src/providers/wcs/qgswcscapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,18 @@ bool QgsWcsCapabilities::parseDescribeCoverageDom11( QByteArray const &xml, QgsW
}
else
{
QgsRectangle box( low[0], low[1], high[0], high[1] ) ;
QgsRectangle box;
QgsCoordinateReferenceSystem crs;
if ( crs.createFromOgcWmsCrs( authid ) && crs.axisInverted() )
{
box = QgsRectangle( low[1], low[0], high[1], high[0] );
}
else
{
box = QgsRectangle( low[0], low[1], high[0], high[1] );
}
coverage->boundingBoxes.insert( authid, box );
QgsDebugMsg( "crs: " + crs.authid() + " " + crs.description() + QString( " axisInverted = %1" ).arg( crs.axisInverted() ) );
QgsDebugMsg( "BoundingBox: " + authid + " : " + box.toString() );
}
}
Expand Down
93 changes: 66 additions & 27 deletions src/providers/wcs/qgswcsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,9 @@ QgsWcsProvider::QgsWcsProvider( QString const &uri )
mFixBox = true;
QgsDebugMsg( "Test response size is smaller by pixel, using mFixBox" );
}
// Geoserver is sometimes giving rotated raster (probably for geographic CRS,
// switched axis?)
// Geoserver is giving rotated raster for geographic CRS - switched axis,
// Geoserver developers argue that changed axis order applies also to
// returned raster, that is exagerated IMO but we have to handle that.
if (( responseWidth == requestHeight && responseHeight == requestWidth ) ||
( responseWidth == requestHeight - 1 && responseHeight == requestWidth - 1 ) )
{
Expand Down Expand Up @@ -546,33 +547,47 @@ void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, in

if ( mCachedGdalDataset )
{
int width = GDALGetRasterXSize( mCachedGdalDataset );
int height = GDALGetRasterYSize( mCachedGdalDataset );

// It may happen (Geoserver) that if requested BBOX is larger than coverage
// extent, the returned data cover intersection of requested BBOX and coverage
// extent scaled to requested WIDTH/HEIGHT => check extent
// Unfortunately if received raster does not hac CRS, the extent is raster size
// and in that case it cannot be used to verify extent
QgsCoordinateReferenceSystem cacheCrs;
if ( !cacheCrs.createFromWkt( GDALGetProjectionRef( mCachedGdalDataset ) ) &&
!cacheCrs.createFromWkt( GDALGetGCPProjection( mCachedGdalDataset ) ) )
{
QgsDebugMsg( "Cached does not have CRS" );
}
QgsDebugMsg( "Cache CRS: " + cacheCrs.authid() + " " + cacheCrs.description() );

QgsRectangle cacheExtent = QgsGdalProviderBase::extent( mCachedGdalDataset );
QgsDebugMsg( "viewExtent = " + viewExtent.toString() );
QgsDebugMsg( "cacheExtent = " + cacheExtent.toString() );
// using doubleNear is too precise, example accetable difference:
// 179.9999999306699863 x 179.9999999306700431
if ( !doubleNearSig( cacheExtent.xMinimum(), viewExtent.xMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.yMinimum(), viewExtent.yMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.xMaximum(), viewExtent.xMaximum(), 10 ) ||
!doubleNearSig( cacheExtent.yMaximum(), viewExtent.yMaximum(), 10 ) )
// TODO: check also rotated
if ( cacheCrs.isValid() && !mFixRotate )
{
QgsDebugMsg( "cacheExtent and viewExtent differ" );
QgsMessageLog::logMessage( tr( "Received coverage has wrong extent %1 (expected %2)" ).arg( cacheExtent.toString() ).arg( viewExtent.toString() ), tr( "WCS" ) );
// We are doing all possible to avoid this situation,
// If it happens, it would be possible to rescale the portion we get
// to only part of the data block, but it is better to left it
// blank, so that the problem may be discovered in its origin.
return;
// using doubleNear is too precise, example accetable difference:
// 179.9999999306699863 x 179.9999999306700431
if ( !doubleNearSig( cacheExtent.xMinimum(), viewExtent.xMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.yMinimum(), viewExtent.yMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.xMaximum(), viewExtent.xMaximum(), 10 ) ||
!doubleNearSig( cacheExtent.yMaximum(), viewExtent.yMaximum(), 10 ) )
{
QgsDebugMsg( "cacheExtent and viewExtent differ" );
QgsMessageLog::logMessage( tr( "Received coverage has wrong extent %1 (expected %2)" ).arg( cacheExtent.toString() ).arg( viewExtent.toString() ), tr( "WCS" ) );
// We are doing all possible to avoid this situation,
// If it happens, it would be possible to rescale the portion we get
// to only part of the data block, but it is better to left it
// blank, so that the problem may be discovered in its origin.
return;
}
}

GDALRasterBandH gdalBand = GDALGetRasterBand( mCachedGdalDataset, bandNo );
int width = GDALGetRasterXSize( mCachedGdalDataset );
int height = GDALGetRasterYSize( mCachedGdalDataset );
QgsDebugMsg( QString( "cached data width = %1 height = %2 (expected %3 x %4)" ).arg( width ).arg( height ).arg( pixelWidth ).arg( pixelHeight ) );

GDALRasterBandH gdalBand = GDALGetRasterBand( mCachedGdalDataset, bandNo );
// TODO: check type? , check band count?
if ( mFixRotate && width == pixelHeight && height == pixelWidth )
{
Expand Down Expand Up @@ -687,6 +702,12 @@ void QgsWcsProvider::getCache( int bandNo, QgsRectangle const & viewExtent, int
setQueryItem( url, "COVERAGE", mIdentifier );
if ( !mTime.isEmpty() )
{
// It seems that Mmapserver (6.0.3) WCS 1.1 completely ignores
// TemporalDomain. Some code (copy-pasted from 1.0) is commented in
// msWCSDescribeCoverage_CoverageDescription11() and GetCoverage
// TimeSequence param is not supported at all. If a coverage is defined
// with timeposition in mapfile, the result of GetCoverage is empty
// raster (all values 0).
setQueryItem( url, "TIME", mTime );
}
setQueryItem( url, "BBOX", bbox );
Expand Down Expand Up @@ -736,12 +757,16 @@ void QgsWcsProvider::getCache( int bandNo, QgsRectangle const & viewExtent, int
// GridOffsets WCS 1.1:
// GridType urn:ogc:def:method:WCS:1.1:2dSimpleGrid : 2 values
// GridType urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs : 4 values, the center two of these offsets will be zero when the GridCRS is not rotated or skewed in the GridBaseCRS.
// The second offset must be negative because origin is in upper corner
// The yOff must be negative because origin is in upper corner
// WCS 1.1.2: BaseX = origin(1) + offsets(1) * GridX
// BaseY = origin(2) + offsets(2) * GridY
// otherwise GeoServer gives mirrored result, however
// Mapserver works OK with both positive and negative
double yOff = mFixRotate ? yRes : -yRes;
// OTOH, the yOff offset must not be negative with mFixRotate and Geoserver 2.1-SNAPSHOT
// but it must be negative with GeoServer 2.1.3 and mFixRotate. I am not sure
// at this moment 100% -> disabling positive yOff for now - TODO: try other servers
//double yOff = mFixRotate ? yRes : -yRes; // this was working with some servers I think
double yOff = -yRes;
QString gridOffsets = QString( changeXY ? "%2,%1" : "%1,%2" )
//setQueryItem( url, "GRIDTYPE", "urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs" );
//QString gridOffsets = QString( changeXY ? "%2,0,0,%1" : "%1,0,0,%2" )
Expand Down Expand Up @@ -1420,13 +1445,27 @@ bool QgsWcsProvider::calculateExtent()
QgsRectangle cacheExtent = QgsGdalProviderBase::extent( mCachedGdalDataset );
QgsDebugMsg( "mCoverageExtent = " + mCoverageExtent.toString() );
QgsDebugMsg( "cacheExtent = " + cacheExtent.toString() );
if ( !doubleNearSig( cacheExtent.xMinimum(), mCoverageExtent.xMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.yMinimum(), mCoverageExtent.yMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.xMaximum(), mCoverageExtent.xMaximum(), 10 ) ||
!doubleNearSig( cacheExtent.yMaximum(), mCoverageExtent.yMaximum(), 10 ) )
QgsCoordinateReferenceSystem cacheCrs;
if ( !cacheCrs.createFromWkt( GDALGetProjectionRef( mCachedGdalDataset ) ) &&
!cacheCrs.createFromWkt( GDALGetGCPProjection( mCachedGdalDataset ) ) )
{
QgsDebugMsg( "cacheExtent and mCoverageExtent differ, mCoverageExtent cut to cacheExtent" );
mCoverageExtent = cacheExtent;
QgsDebugMsg( "Cached does not have CRS" );
}
QgsDebugMsg( "Cache CRS: " + cacheCrs.authid() + " " + cacheCrs.description() );

// We can only verify extent if CRS is set
// If dataset comes rotated, GDAL probably cuts latitude extend, disable
// extent check for rotated, TODO: verify
if ( cacheCrs.isValid() && !mFixRotate )
{
if ( !doubleNearSig( cacheExtent.xMinimum(), mCoverageExtent.xMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.yMinimum(), mCoverageExtent.yMinimum(), 10 ) ||
!doubleNearSig( cacheExtent.xMaximum(), mCoverageExtent.xMaximum(), 10 ) ||
!doubleNearSig( cacheExtent.yMaximum(), mCoverageExtent.yMaximum(), 10 ) )
{
QgsDebugMsg( "cacheExtent and mCoverageExtent differ, mCoverageExtent cut to cacheExtent" );
mCoverageExtent = cacheExtent;
}
}
}
else
Expand Down
17 changes: 16 additions & 1 deletion tests/src/providers/wcs-servers.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,22 @@
offender: 'server',
coverages: [ 'modis-001' ],
versions: [ '1.0.0' ],
description: "The server DescribeCoverage advertises temporalDomain.timePosition 2002-001, but GetCoverages fails with 'msWCSGetCoverage(): WCS server error. Underlying layer is not tiled, unable to do temporal subsetting.' if TIME=2002-001 is used."
description: "The server DescribeCoverage advertises temporalDomain.timePosition 2002-001, but GetCoverage fails with 'msWCSGetCoverage(): WCS server error. Underlying layer is not tiled, unable to do temporal subsetting.' if TIME=2002-001 is used."
}
]
}, {
url: 'http://geoserver-dev-1.irradiare.com:8080/geoserver/ows',
issues: [
{
offender: 'server',
versions: [ '1.0.0' ],
description: "The server fails in GetCapabilities with 'java.io.IOException
null Translator error No input stream for the provided source...'"
},{
offender: 'server',
coverages: [ 'Arc_Sample' ],
versions: [ '1.1.1' ],
description: "The problem seems to be that some versions of Geoserver (2.1.3) require latitude in GRIDOFFSETS to be negative and other versions (2.1-SNAPSHOT) positive. BTW: GetCoverage returns raster without CRS and thues the extent means raster size and cannot be used to verify extent"
}
]
}, {
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ ADD_PYTHON_TEST(PyQgsComposerHtml test_qgscomposerhtml.py)
ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py)
ADD_PYTHON_TEST(PyQgsAnalysis test_qgsanalysis.py)
#ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)
ADD_PYTHON_TEST(PyQgsSymbolLayerV2 test_qgssymbollayerv2.py)
562 changes: 562 additions & 0 deletions tests/src/python/test_qgssymbollayerv2.py

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions tests/testdata/symbol_layer/QgsCentroidFillSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>PAR022</se:Name>
<UserStyle>
<se:Name>PAR022</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PointSymbolizer>
<se:Graphic>
<se:Mark>
<se:WellKnownName>regular_star</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">#55aaff</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#00ff00</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>2</se:Size>
</se:Graphic>
</se:PointSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
30 changes: 30 additions & 0 deletions tests/testdata/symbol_layer/QgsEllipseSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cs000</se:Name>
<UserStyle>
<se:Name>022cs000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PointSymbolizer>
<se:Graphic>
<se:Mark>
<se:WellKnownName>circle</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">#ffff7f</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#aaaaff</se:SvgParameter>
<se:SvgParameter name="stroke-width">1</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>7</se:Size>
<VendorOption name="widthHeightFactor">1.4</VendorOption>
</se:Graphic>
</se:PointSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
30 changes: 30 additions & 0 deletions tests/testdata/symbol_layer/QgsFontMarkerSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cs000</se:Name>
<UserStyle>
<se:Name>022cs000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PointSymbolizer>
<se:Graphic>
<se:Mark>
<OnlineResource xlink:type="simple" xlink:href="ttf://Arial"/>
<Format>ttf</Format>
<se:MarkIndex>77</se:MarkIndex>
<se:Fill>
<se:SvgParameter name="fill">#000000</se:SvgParameter>
</se:Fill>
</se:Mark>
<se:Size>6.23</se:Size>
<se:Rotation>
<ogc:Literal>3</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:PointSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
31 changes: 31 additions & 0 deletions tests/testdata/symbol_layer/QgsLineDecorationSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cl000</se:Name>
<UserStyle>
<se:Name>022cl000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:LineSymbolizer>
<se:Stroke>
<se:GraphicStroke>
<se:Graphic>
<se:Mark>
<se:WellKnownName>arrowhead</se:WellKnownName>
<se:Stroke>
<se:SvgParameter name="stroke">#aaaa7f</se:SvgParameter>
<se:SvgParameter name="stroke-width">2.26</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>18.08</se:Size>
</se:Graphic>
</se:GraphicStroke>
</se:Stroke>
<VendorOption name="placement">lastPoint</VendorOption>
</se:LineSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
33 changes: 33 additions & 0 deletions tests/testdata/symbol_layer/QgsLinePatternFillSymbolLayer.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>PAR022</se:Name>
<UserStyle>
<se:Name>PAR022</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PolygonSymbolizer>
<se:Fill>
<se:GraphicFill>
<se:Graphic>
<se:Mark>
<se:WellKnownName>horline</se:WellKnownName>
<se:Stroke>
<se:SvgParameter name="stroke">#ff55ff</se:SvgParameter>
<se:SvgParameter name="stroke-width">1.5</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>4</se:Size>
<se:Rotation>
<ogc:Literal>57</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:GraphicFill>
</se:Fill>
</se:PolygonSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
33 changes: 33 additions & 0 deletions tests/testdata/symbol_layer/QgsMarkerLineSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cl000</se:Name>
<UserStyle>
<se:Name>022cl000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:LineSymbolizer>
<VendorOption name="placement">centralPoint</VendorOption>
<se:Stroke>
<se:GraphicStroke>
<se:Graphic>
<se:Mark>
<se:WellKnownName>circle</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">#ff0000</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#000000</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>2</se:Size>
</se:Graphic>
</se:GraphicStroke>
</se:Stroke>
</se:LineSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
36 changes: 36 additions & 0 deletions tests/testdata/symbol_layer/QgsPointPatternFillSymbolLayer.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>PolygonLayer</se:Name>
<UserStyle>
<se:Name>PolygonLayer</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PolygonSymbolizer>
<se:Fill>
<se:GraphicFill>
<se:Graphic>
<se:Mark>
<se:WellKnownName>triangle</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">#ffaa00</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#ff007f</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>3</se:Size>
<se:Rotation>
<ogc:Literal>5</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:GraphicFill>
</se:Fill>
<VendorOption name="distance">15,15</VendorOption>
</se:PolygonSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
41 changes: 41 additions & 0 deletions tests/testdata/symbol_layer/QgsSVGFillSymbolLayer.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>PolygonLayer</se:Name>
<UserStyle>
<se:Name>PolygonLayer</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PolygonSymbolizer>
<se:Fill>
<se:GraphicFill>
<se:Graphic>
<se:ExternalGraphic>
<OnlineResource xlink:type="simple" xlink:href="C:/Program Files/qgis1.8.0/./svg/accommodation/accommodation_camping.svg"/>
<Format>image/svg+xml</Format>
</se:ExternalGraphic>
<se:Size>6</se:Size>
<se:SvgParameter name="stroke">#000000</se:SvgParameter>
<se:SvgParameter name="stroke-width">3</se:SvgParameter>
<se:Rotation>
<ogc:Literal>4</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:GraphicFill>
</se:Fill>
</se:PolygonSymbolizer>
<se:LineSymbolizer>
<se:Stroke>
<se:SvgParameter name="stroke">#000000</se:SvgParameter>
<se:SvgParameter name="stroke-width">0.26</se:SvgParameter>
<se:SvgParameter name="stroke-linejoin">bevel</se:SvgParameter>
<se:SvgParameter name="stroke-linecap">square</se:SvgParameter>
<se:SvgParameter name="stroke-dasharray">5 2</se:SvgParameter>
</se:Stroke>
</se:LineSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
24 changes: 24 additions & 0 deletions tests/testdata/symbol_layer/QgsSimpleFillSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cp000</se:Name>
<UserStyle>
<se:Name>022cp000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PolygonSymbolizer>
<se:Fill>
<se:SvgParameter name="fill">#aa55ff</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#ffaa7f</se:SvgParameter>
<se:SvgParameter name="stroke-width">0.26</se:SvgParameter>
<se:SvgParameter name="stroke-dasharray">1 2</se:SvgParameter>
</se:Stroke>
</se:PolygonSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
23 changes: 23 additions & 0 deletions tests/testdata/symbol_layer/QgsSimpleLineSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cl000</se:Name>
<UserStyle>
<se:Name>022cl000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:LineSymbolizer>
<se:Stroke>
<se:SvgParameter name="stroke">#aa007f</se:SvgParameter>
<se:SvgParameter name="stroke-width">1.26</se:SvgParameter>
<se:SvgParameter name="stroke-linejoin">mitre</se:SvgParameter>
<se:SvgParameter name="stroke-linecap">round</se:SvgParameter>
<se:SvgParameter name="stroke-dasharray">5 2</se:SvgParameter>
</se:Stroke>
</se:LineSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
31 changes: 31 additions & 0 deletions tests/testdata/symbol_layer/QgsSimpleMarkerSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cs000</se:Name>
<UserStyle>
<se:Name>022cs000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PointSymbolizer>
<se:Graphic>
<se:Mark>
<se:WellKnownName>pentagon</se:WellKnownName>
<se:Fill>
<se:SvgParameter name="fill">#68a0f4</se:SvgParameter>
</se:Fill>
<se:Stroke>
<se:SvgParameter name="stroke">#5500ff</se:SvgParameter>
</se:Stroke>
</se:Mark>
<se:Size>6</se:Size>
<se:Rotation>
<ogc:Literal>10</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:PointSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
26 changes: 26 additions & 0 deletions tests/testdata/symbol_layer/QgsSvgMarkerSymbolLayerV2.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cs000</se:Name>
<UserStyle>
<se:Name>022cs000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<se:PointSymbolizer>
<se:Graphic>
<se:ExternalGraphic>
<OnlineResource xlink:type="simple" xlink:href="C:/Program Files/qgis1.8.0/svg/gpsicons/skull.svg"/>
<Format>image/svg+xml</Format>
</se:ExternalGraphic>
<se:Size>12</se:Size>
<se:Rotation>
<ogc:Literal>45</ogc:Literal>
</se:Rotation>
</se:Graphic>
</se:PointSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
24 changes: 24 additions & 0 deletions tests/testdata/symbol_layer/QgsVectorFieldSymbolLayer.sld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" xmlns:se="http://www.opengis.net/se">
<NamedLayer>
<se:Name>022cs000</se:Name>
<UserStyle>
<se:Name>022cs000</se:Name>
<se:FeatureTypeStyle>
<se:Rule>
<se:Name>Single symbol</se:Name>
<!--VectorField not implemented yet...-->
<se:LineSymbolizer>
<se:Stroke>
<se:SvgParameter name="stroke">#000000</se:SvgParameter>
<se:SvgParameter name="stroke-width">0.26</se:SvgParameter>
<se:SvgParameter name="stroke-linejoin">bevel</se:SvgParameter>
<se:SvgParameter name="stroke-linecap">square</se:SvgParameter>
<se:SvgParameter name="stroke-dasharray">5 2</se:SvgParameter>
</se:Stroke>
</se:LineSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>