WMS CRS:84 is not allowed

1908
4
Jump to solution
04-26-2018 03:53 AM
MarvinKalani
New Contributor II

WMS Layer not showing up, because CRS:84 is used instead of EPSG:4326

In Fiddler I can see that the requested WMS url uses : &crs=CRS%3A84& but it should be &crs=EPSG%3A4326 instead

How can I force the request to use the correct projection?

In ArcgisRuntime 10.2.7 the correct projection EPSG:4326 was used, now in Version 100.x.x always the unallowed CRS:84 is used.

0 Kudos
1 Solution

Accepted Solutions
MatveiStefarov
Esri Contributor

Thank you for sharing more information! We will continue investigating, to see if the default behavior of WmsLayer can be improved in the future. I also have a workaround you can use right now.

Runtime SDK for .NET includes a global ArcGISHttpClientHandler.HttpRequestBegin event. This event is raised before Runtime makes a network request, and allows application developers to examine or modify the request parameters. By listening to this event you can detect WMS requests that use "CRS:84" and change them to use "EPSG:4326" instead. Here is an example implementation:

private static void ReplaceCrs84WithEpsg4326(object sender, HttpRequestMessage e)
{
    string rawQuery = e.RequestUri.Query;

    // Skip requests without a query
    if (string.IsNullOrEmpty(rawQuery))
        return;

    // For convenience, parse query as a Dictionary
    var queryParameters = System.Web.HttpUtility.ParseQueryString(rawQuery);
    var queryDict = queryParameters.AllKeys.ToDictionary(
        k => k.ToUpperInvariant(),
        k => queryParameters[k].ToUpperInvariant());

    // Skip requests that are not WMS
    string serviceParam;
    if (!queryDict.TryGetValue("SERVICE", out serviceParam) || serviceParam!= "WMS")
        return;

    // Skip requests that don't use CRS:84
    string crsParam;
    if (!queryDict.TryGetValue("CRS", out crsParam) || crsParam != "CRS:84")
        return;

    // TODO: if necessary, apply additional checks here to limit which WMS services are affected

    // Replace "CRS:84" with "EPSG:4326"
    queryParameters["CRS"] = "EPSG:4326";

    // Swap lat/lon in the requested coordinates
    var bbox = queryDict["BBOX"].Split(',');
    var swappedBbox = bbox[1] + ',' + bbox[0] + ',' + bbox[3] + ',' + bbox[2];
    queryParameters["BBOX"] = swappedBbox;

    // Replace the original query with our modified one
    var uriWithoutParams = e.RequestUri.AbsoluteUri.Split('?').First();
    e.RequestUri = new Uri(uriWithoutParams + '?' + queryParameters);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And here is how you can connect it (this only needs to be done once):

ArcGISHttpClientHandler.HttpRequestBegin += ReplaceCrs84WithEpsg4326;

With this workaround in place, I was able to use any layer from the University of Heidelberg WMS service. I hope that it works for your service too.

View solution in original post

4 Replies
MatveiStefarov
Esri Contributor

I'm sorry to hear that you're having trouble. The name "CRS:84" is identical to "EPSG:4326" except for the axis order (lat-lon vs lon-lat). The WMS 1.3.0 specification recommends that all servers support both. Runtime 100.x does not currently expose any API for choosing one name over the other, since both names map to SpatialReferences.Wgs84. WMSLayer prefers sending "CRS:84" because we found it to be the most widely-used default, and because other Esri products already make the same preference.

It might be possible to improve Runtime's compatibility with services that accept "EPSG:4326" but reject "CRS:84". I'm afraid that I could not find any software/service with this behavior. Are you able share more details about this particular WMS service (e.g. its URL, software details, a copy of the GetCapabilities response)?

0 Kudos
MarvinKalani
New Contributor II

GetCapabilities looks like this:

<Layer>
    <Title>WMS</Title>
    <CRS>EPSG:4326</CRS>
    <BoundingBox CRS="EPSG:4326" minx="-90" miny="-180" maxx="90" maxy="180" />
</Layer>

In my point of view you should not use other CRS definitions than given in the specific layer, otherwise you will get an :

<ServiceException code="InvalidCRS">unsupported crs: CRS:84</ServiceException>

If the Layer contains CRS:84 in the <CRS> tag it would be ok:

<Layer>
    <Title>WMS</Title>
    <CRS>EPSG:4326</CRS>

    <CRS>CRS:84</CRS>

    <BoundingBox CRS="EPSG:4326" minx="-90" miny="-180" maxx="90" maxy="180" />   

    <BoundingBox CRS="CRS:84" minx="-180" miny="-90" maxx="180" maxy="90" />
</Layer>

If the Layer CRS tag does not contain CRS:84 it would NOT be ok and would also end up in the ServiceException:

<Layer>
    <Title>WMS</Title>
    <CRS>EPSG:4326</CRS>

    <BoundingBox CRS="EPSG:4326" minx="-90" miny="-180" maxx="90" maxy="180" />   

    <BoundingBox CRS="CRS:84" minx="-180" miny="-90" maxx="180" maxy="90" />
</Layer>

You can try it out here (its just an example I found):

this is fine :

http://129.206.228.72/cached/osm?LAYERS=osm_auto:all&STYLES=&CRS=EPSG%3A4326&FORMAT=image%2Fpng&SERV...

this is causing "unsupported crs: CRS:84":

http://129.206.228.72/cached/osm?LAYERS=osm_auto:all&STYLES=&CRS=CRS%3A84&FORMAT=image%2Fpng&SERVICE...

The GetCapabilities does not show CRS:84 as possible <CRS> Tag even if the bounding boxes would let you think you can use it:

http://129.206.228.72/cached/osm?Request=GetCapabilities&service=WMS&version=1.3.0

0 Kudos
MatveiStefarov
Esri Contributor

Thank you for sharing more information! We will continue investigating, to see if the default behavior of WmsLayer can be improved in the future. I also have a workaround you can use right now.

Runtime SDK for .NET includes a global ArcGISHttpClientHandler.HttpRequestBegin event. This event is raised before Runtime makes a network request, and allows application developers to examine or modify the request parameters. By listening to this event you can detect WMS requests that use "CRS:84" and change them to use "EPSG:4326" instead. Here is an example implementation:

private static void ReplaceCrs84WithEpsg4326(object sender, HttpRequestMessage e)
{
    string rawQuery = e.RequestUri.Query;

    // Skip requests without a query
    if (string.IsNullOrEmpty(rawQuery))
        return;

    // For convenience, parse query as a Dictionary
    var queryParameters = System.Web.HttpUtility.ParseQueryString(rawQuery);
    var queryDict = queryParameters.AllKeys.ToDictionary(
        k => k.ToUpperInvariant(),
        k => queryParameters[k].ToUpperInvariant());

    // Skip requests that are not WMS
    string serviceParam;
    if (!queryDict.TryGetValue("SERVICE", out serviceParam) || serviceParam!= "WMS")
        return;

    // Skip requests that don't use CRS:84
    string crsParam;
    if (!queryDict.TryGetValue("CRS", out crsParam) || crsParam != "CRS:84")
        return;

    // TODO: if necessary, apply additional checks here to limit which WMS services are affected

    // Replace "CRS:84" with "EPSG:4326"
    queryParameters["CRS"] = "EPSG:4326";

    // Swap lat/lon in the requested coordinates
    var bbox = queryDict["BBOX"].Split(',');
    var swappedBbox = bbox[1] + ',' + bbox[0] + ',' + bbox[3] + ',' + bbox[2];
    queryParameters["BBOX"] = swappedBbox;

    // Replace the original query with our modified one
    var uriWithoutParams = e.RequestUri.AbsoluteUri.Split('?').First();
    e.RequestUri = new Uri(uriWithoutParams + '?' + queryParameters);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And here is how you can connect it (this only needs to be done once):

ArcGISHttpClientHandler.HttpRequestBegin += ReplaceCrs84WithEpsg4326;

With this workaround in place, I was able to use any layer from the University of Heidelberg WMS service. I hope that it works for your service too.

MarvinKalani
New Contributor II

Yes, it works like a charm. Thank you very much.

0 Kudos