Skip to content

Commit

Permalink
qgswmsgetcapabilities: Use float format for min/max scale parameters
Browse files Browse the repository at this point in the history
`MinScaleDenominator` and `MaxScaleDenominator` string are created
from a `QString::number` call with the default parameters. In that
case, the most concise format is used. This can result in the
scientific notation being used for very big or small numbers.

With this change, these parameters are now always displayed in a float
format. Reasons for this change:
- this might prevent a loss of precision for very small or big numbers
- this output is not supposed to be human readable so it does not
 matter if is not concise
- this is the logic used by a lot of WMS servers, for example arcgis
 server
  • Loading branch information
ptitjano committed Jan 7, 2025
1 parent 6f4df5d commit 7a3b757
Show file tree
Hide file tree
Showing 4 changed files with 1,244 additions and 4 deletions.
15 changes: 11 additions & 4 deletions src/server/services/wms/qgswmsgetcapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,25 +1048,32 @@ namespace QgsWms
//min/max scale denominatorScaleBasedVisibility
if ( layerInfos.hasScaleBasedVisibility )
{
// Convert double to string and remove trailing zero and last point if present
auto formatScale = []( double value ) {
const thread_local QRegularExpression trailingZeroRegEx = QRegularExpression( QStringLiteral( "0+$" ) );
const thread_local QRegularExpression trailingPointRegEx = QRegularExpression( QStringLiteral( "[.]+$" ) );
return QString::number( value, 'f' ).remove( trailingZeroRegEx ).remove( trailingPointRegEx );
};

if ( version == QLatin1String( "1.1.1" ) )
{
double OGC_PX_M = 0.00028; // OGC reference pixel size in meter, also used by qgis
double SCALE_TO_SCALEHINT = OGC_PX_M * M_SQRT2;

QDomElement scaleHintElem = doc.createElement( QStringLiteral( "ScaleHint" ) );
scaleHintElem.setAttribute( QStringLiteral( "min" ), QString::number( layerInfos.maxScale * SCALE_TO_SCALEHINT ) );
scaleHintElem.setAttribute( QStringLiteral( "max" ), QString::number( layerInfos.minScale * SCALE_TO_SCALEHINT ) );
scaleHintElem.setAttribute( QStringLiteral( "min" ), formatScale( layerInfos.maxScale * SCALE_TO_SCALEHINT ) );
scaleHintElem.setAttribute( QStringLiteral( "max" ), formatScale( layerInfos.minScale * SCALE_TO_SCALEHINT ) );
layerElem.appendChild( scaleHintElem );
}
else
{
QDomElement minScaleElem = doc.createElement( QStringLiteral( "MinScaleDenominator" ) );
QDomText minScaleText = doc.createTextNode( QString::number( layerInfos.maxScale ) );
QDomText minScaleText = doc.createTextNode( formatScale( layerInfos.maxScale ) );
minScaleElem.appendChild( minScaleText );
layerElem.appendChild( minScaleElem );

QDomElement maxScaleElem = doc.createElement( QStringLiteral( "MaxScaleDenominator" ) );
QDomText maxScaleText = doc.createTextNode( QString::number( layerInfos.minScale ) );
QDomText maxScaleText = doc.createTextNode( formatScale( layerInfos.minScale ) );
maxScaleElem.appendChild( maxScaleText );
layerElem.appendChild( maxScaleElem );
}
Expand Down
34 changes: 34 additions & 0 deletions tests/src/python/test_qgsserver_wms.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,40 @@ def test_getcapabilities_advertised_url(self):
b"\n".join(headers) + b"\n\n" + bytes(response.body()), expected
)

def test_getcapabilities_scale_denominator(self):
server = QgsServer()
request = QgsServerRequest()
projectPath = os.path.join(
self.testdata_path, "test_project_scale_denominator.qgs"
)
request.setUrl(
QUrl(
"http://localhost/qgis_mapserv.fcgi?MAP="
+ projectPath
+ "&SERVICE=WMS&REQUEST=GetCapabilities"
)
)
request.setOriginalUrl(QUrl("http://localhost/wms/test_project"))
response = QgsBufferServerResponse()
server.handleRequest(request, response)
response.flush()

headers = []
rh = response.headers()
rk = sorted(rh.keys())
for k in rk:
headers.append((f"{k}: {rh[k]}").encode())

reference_path = os.path.join(
self.testdata_path, "wms_getcapabilities_scale_denominator.txt"
)
f = open(reference_path, "rb")
expected = f.read()
print(bytes(response.body()))
self.assertXMLEqual(
b"\n".join(headers) + b"\n\n" + bytes(response.body()), expected
)

def test_getprojectsettings(self):
self.wms_request_compare("GetProjectSettings")

Expand Down
Loading

0 comments on commit 7a3b757

Please sign in to comment.