Skip to content

Commit

Permalink
Merge pull request #414 from Netherlands3D/fix/wms-bounding-boxes
Browse files Browse the repository at this point in the history
Bounding boxes were not correctly determined because the CRS was not …
  • Loading branch information
TomSimons authored Feb 14, 2025
2 parents 3562712 + c96ea4e commit c4e4ee8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 26 deletions.
14 changes: 10 additions & 4 deletions Assets/_Functionalities/Wms/Scripts/WMSTileDataLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,16 @@ private BoundingBox DetermineBoundingBox(TileChange tileChange, MapFilters mapFi
var bottomLeft = new Coordinate(CoordinateSystem.RD, tileChange.X, tileChange.Y, 0);
var topRight = new Coordinate(CoordinateSystem.RD, tileChange.X + tileSize, tileChange.Y + tileSize, 0);

var splitReferenceCode = mapFilters.spatialReference.Split(':');
string coordinateSystemAsString = splitReferenceCode[0].ToLower() == "epsg"
? splitReferenceCode[^1]
: DefaultEpsgCoordinateSystem;
// Yes, there is a semicolon missing, this is on purpose because FindCoordinateSystem finds this and not
// with the semicolon
string coordinateSystemAsString = "CRS84";
if (mapFilters.spatialReference != "CRS:84")
{
var splitReferenceCode = mapFilters.spatialReference.Split(':');
coordinateSystemAsString = splitReferenceCode[0].ToLower() == "epsg"
? splitReferenceCode[^1]
: DefaultEpsgCoordinateSystem;
}

CoordinateSystems.FindCoordinateSystem(coordinateSystemAsString, out var foundCoordinateSystem);

Expand Down
68 changes: 46 additions & 22 deletions Assets/_Functionalities/Wms/Scripts/WmsGetCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,31 @@ public BoundingBoxContainer GetBounds()
string layerName = layerNode.SelectSingleNode("*[local-name()='Name']", namespaceManager)?.InnerText;
if (string.IsNullOrEmpty(layerName)) continue;

var boundingBoxNode = layerNode.SelectSingleNode("*[local-name()='BoundingBox']", namespaceManager);
if (boundingBoxNode != null)
// We prefer EPSG:28992 because it matches our application better ..
var boundingBoxNode = layerNode.SelectSingleNode("*[local-name()='BoundingBox' and @CRS='EPSG:28992']", namespaceManager);
// .. Ok, WGS84 is Okay too ..
boundingBoxNode ??= layerNode.SelectSingleNode("*[local-name()='BoundingBox' and @CRS='EPSG:3857']", namespaceManager);
// .. Seriously? That one isn't there either? Ok, let's pick the first and see what gives
boundingBoxNode ??= layerNode.SelectSingleNode("*[local-name()='BoundingBox']", namespaceManager);

// Wait what? Nothing?
if (boundingBoxNode == null) continue;

string crsString = boundingBoxNode.Attributes["CRS"].Value;
// A hack used to aid identifying the correct CRS, the crsString that our coordinate converter
// looks for is CRS84 and not CRS:84. The latter is the actual designation. Please see Annex B of
// the WMS specification https://portal.ogc.org/files/?artifact_id=14416 to see more information
// on the use of the CRS namespace -hence the colon between CRS and 84.
if (crsString == "CRS:84") crsString = "CRS84";

var hasCRS = CoordinateSystems.FindCoordinateSystem(crsString, out var crs);
if (!hasCRS)
{
string crsString = boundingBoxNode.Attributes["CRS"]?.Value;
var hasCRS = CoordinateSystems.FindCoordinateSystem(crsString, out var crs);

if (!hasCRS)
{
crs = CoordinateSystem.CRS84; //default
Debug.LogWarning("Custom CRS BBox found, but not able to be parsed, defaulting to WGS84 CRS. Founds CRS string: " + crsString);
}

container.LayerBoundingBoxes[layerName] = ParseBoundingBox(boundingBoxNode, crs);
crs = CoordinateSystem.CRS84; //default
Debug.LogWarning("CRS for BBox could not be recognized, defaulting to CRS:84. Found string: " + crsString);
}

container.LayerBoundingBoxes[layerName] = ParseBoundingBox(boundingBoxNode, crs);
}

return container;
Expand All @@ -117,17 +128,26 @@ private BoundingBox ParseBoundingBox(XmlNode node, CoordinateSystem crs)
if (node == null)
return null;

var minXString = node.SelectSingleNode("*[local-name()='westBoundLongitude' or @minx]", namespaceManager)?.InnerText;
var minYString = node.SelectSingleNode("*[local-name()='southBoundLatitude' or @miny]", namespaceManager)?.InnerText;
var maxXString = node.SelectSingleNode("*[local-name()='eastBoundLongitude' or @maxx]", namespaceManager)?.InnerText;
var maxYString = node.SelectSingleNode("*[local-name()='northBoundLatitude' or @maxy]", namespaceManager)?.InnerText;

//replace , with . to ensure the parse function works as intended.
// This method seems to be reused in reading the EX_GeographicBoundingBox -which is always in CRS:84 but I
// hope the caller does this correct-
var minXString = node.SelectSingleNode("*[local-name()='westBoundLongitude']", namespaceManager)?.InnerText;
var minYString = node.SelectSingleNode("*[local-name()='southBoundLatitude']", namespaceManager)?.InnerText;
var maxXString = node.SelectSingleNode("*[local-name()='eastBoundLongitude']", namespaceManager)?.InnerText;
var maxYString = node.SelectSingleNode("*[local-name()='northBoundLatitude']", namespaceManager)?.InnerText;

// Ugly solution to support both EX_GeographicBoundingBox and BoundingBox, the latter uses attributes
minXString ??= node.Attributes["minx"]?.Value;
minYString ??= node.Attributes["miny"]?.Value;
maxXString ??= node.Attributes["maxx"]?.Value;
maxYString ??= node.Attributes["maxy"]?.Value;

// replace , with . to ensure the parse function works as intended because some Dutch agencies use the wrong
// decimal separators.
minXString = minXString?.Replace(',', '.');
minYString = minXString?.Replace(',', '.');
maxXString = minXString?.Replace(',', '.');
maxYString = minXString?.Replace(',', '.');
minYString = minYString?.Replace(',', '.');
maxXString = maxXString?.Replace(',', '.');
maxYString = maxYString?.Replace(',', '.');

if (!double.TryParse(minXString, out var minX))
return null;
if (!double.TryParse(minYString, out var minY))
Expand Down Expand Up @@ -167,6 +187,10 @@ public List<MapFilters> GetMaps(int width, int height, bool transparent)
// CRS/SRS may be defined in the current MapNode, but can also inherit from a parent if it is not
// specified the flag at the end of this function will check the current node and its parents
var spatialReference = GetInnerTextForNode(mapNode, mapTemplate.spatialReferenceType, true);

// TODO: Really ugly fix to deal issues around EPSG:4326. So we fixate on CRS:84 now, hoping that will
// work in all situations
spatialReference = "CRS:84";

var map = new MapFilters()
{
Expand Down

0 comments on commit c4e4ee8

Please sign in to comment.