private XmlElement CreateLayerElement(Models.Layer layer, string tileMatrixSetIdentifier) { var layerElement = doc.CreateElement(String.Empty, "Layer", WmtsNamespaceUri); var titleElement = doc.CreateElement(OwsPrefix, "Title", OwsNamespaceUri); titleElement.InnerText = layer.Title; layerElement.AppendChild(titleElement); // TODO: Abstract element var identifierElement = doc.CreateElement(OwsPrefix, "Identifier", OwsNamespaceUri); identifierElement.InnerText = layer.Identifier; layerElement.AppendChild(identifierElement); var styleElement = doc.CreateElement(String.Empty, "Style", WmtsNamespaceUri); var styleIdentifierElement = doc.CreateElement(OwsPrefix, "Identifier", OwsNamespaceUri); styleIdentifierElement.InnerText = "normal"; styleElement.SetAttribute("isDefault", "true"); styleElement.AppendChild(styleIdentifierElement); layerElement.AppendChild(styleElement); var formatElement = doc.CreateElement(String.Empty, "Format", WmtsNamespaceUri); formatElement.InnerText = layer.ContentType; layerElement.AppendChild(formatElement); var wgs84BoundingBoxElement = doc.CreateElement(OwsPrefix, "WGS84BoundingBox", OwsNamespaceUri); var lowerCornerElement = doc.CreateElement(OwsPrefix, "LowerCorner", OwsNamespaceUri); lowerCornerElement.InnerText = "-180.000000 -85.05112878"; // TODO: use actual values wgs84BoundingBoxElement.AppendChild(lowerCornerElement); var upperCornerElement = doc.CreateElement(OwsPrefix, "UpperCorner", OwsNamespaceUri); upperCornerElement.InnerText = "180.000000 85.05112878"; // TODO: use actual values wgs84BoundingBoxElement.AppendChild(upperCornerElement); layerElement.AppendChild(wgs84BoundingBoxElement); var tileMatrixSetLinkElement = doc.CreateElement(String.Empty, "TileMatrixSetLink", WmtsNamespaceUri); var tileMatrixSetElement = doc.CreateElement(String.Empty, "TileMatrixSet", WmtsNamespaceUri); tileMatrixSetElement.InnerText = tileMatrixSetIdentifier; tileMatrixSetLinkElement.AppendChild(tileMatrixSetElement); layerElement.AppendChild(tileMatrixSetLinkElement); return(layerElement); }
/// <summary> /// The TileMap is a cartographically complete map representation. /// </summary> /// <param name="layer">Properties of layer.</param> /// <remarks> /// https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#TileMap_Resource /// </remarks> /// <returns></returns> public XmlDocument GetTileMap(Models.Layer layer) { var doc = new XmlDocument(); var rootElement = doc.CreateElement(String.Empty, Identifiers.TileMapElement, String.Empty); doc.AppendChild(rootElement); var versionAttribute = doc.CreateAttribute(Identifiers.VersionAttribute); versionAttribute.Value = Identifiers.Version100; rootElement.Attributes.Append(versionAttribute); var tilemapservice = $"{this.baseUrl}/{Identifiers.Tms}/{Identifiers.Version100}"; var tilemapserviceAttribute = doc.CreateAttribute("tilemapservice"); tilemapserviceAttribute.Value = tilemapservice; rootElement.Attributes.Append(tilemapserviceAttribute); var titleElement = doc.CreateElement(Identifiers.TitleElement); titleElement.AppendChild(doc.CreateTextNode(layer.Title)); rootElement.AppendChild(titleElement); var abstractElement = doc.CreateElement(Identifiers.AbstractElement); abstractElement.AppendChild(doc.CreateTextNode(layer.Abstract)); rootElement.AppendChild(abstractElement); double unitsWidth, pixelsWidth; switch (layer.Srs) { case Utils.SrsCodes.EPSG3857: { // Assuming 1x1 tile grid at zoom level 0 unitsWidth = 20037508.342789 * 2; pixelsWidth = layer.TileWidth; var srsElement = doc.CreateElement(Identifiers.SrsElement); srsElement.AppendChild(doc.CreateTextNode(Utils.SrsCodes.OSGEO41001)); rootElement.AppendChild(srsElement); // GoogleMapsCompatible tile grid var minX = layer.GeographicalBounds == null ? -20037508.342789 : Utils.WebMercator.X(layer.GeographicalBounds.MinLongitude); var minY = layer.GeographicalBounds == null ? -20037508.342789 : Utils.WebMercator.Y(layer.GeographicalBounds.MinLatitude); var maxX = layer.GeographicalBounds == null ? +20037508.342789 : Utils.WebMercator.X(layer.GeographicalBounds.MaxLongitude); var maxY = layer.GeographicalBounds == null ? +20037508.342789 : Utils.WebMercator.Y(layer.GeographicalBounds.MaxLatitude); var boundingBoxElement = CreateBoundingBoxElement(doc, minX, minY, maxX, maxY, "F6"); rootElement.AppendChild(boundingBoxElement); var originElement = CreateOriginElement(doc, minX, minY, "F6"); rootElement.AppendChild(originElement); break; } case Utils.SrsCodes.EPSG4326: { // Assuming 2x1 tile grid at zoom level 0 unitsWidth = 360; pixelsWidth = layer.TileWidth * 2; var srsElement = doc.CreateElement(Identifiers.SrsElement); srsElement.AppendChild(doc.CreateTextNode(Utils.SrsCodes.EPSG4326)); rootElement.AppendChild(srsElement); var minX = layer.GeographicalBounds == null ? -180 : layer.GeographicalBounds.MinLongitude; var minY = layer.GeographicalBounds == null ? -90 : layer.GeographicalBounds.MinLatitude; var maxX = layer.GeographicalBounds == null ? +180 : layer.GeographicalBounds.MaxLongitude; var maxY = layer.GeographicalBounds == null ? +90 : layer.GeographicalBounds.MaxLatitude; var boundingBoxElement = CreateBoundingBoxElement(doc, minX, minY, maxX, maxY, "F6"); rootElement.AppendChild(boundingBoxElement); var originElement = CreateOriginElement(doc, minX, minY, "F6"); rootElement.AppendChild(originElement); break; } default: { throw new NotImplementedException($"Unknown SRS '{layer.Srs}'"); } } var tileFormatElement = doc.CreateElement(Identifiers.TileFormatElement); var widthAttribute = doc.CreateAttribute("width"); widthAttribute.Value = layer.TileWidth.ToString(CultureInfo.InvariantCulture); tileFormatElement.Attributes.Append(widthAttribute); var heightAttribute = doc.CreateAttribute("height"); heightAttribute.Value = layer.TileHeight.ToString(CultureInfo.InvariantCulture); tileFormatElement.Attributes.Append(heightAttribute); var mimetypeAttribute = doc.CreateAttribute("mime-type"); mimetypeAttribute.Value = layer.ContentType; tileFormatElement.Attributes.Append(mimetypeAttribute); var extensionAttribute = doc.CreateAttribute("extension"); extensionAttribute.Value = layer.Format; tileFormatElement.Attributes.Append(extensionAttribute); rootElement.AppendChild(tileFormatElement); var tileSetsElement = doc.CreateElement("TileSets"); var profileAttribute = CreateProfileAttribute(doc, layer.Srs); tileSetsElement.Attributes.Append(profileAttribute); rootElement.AppendChild(tileSetsElement); // TODO: four tiles at level 0 covering the whole earth for (var level = layer.MinZoom; level <= layer.MaxZoom; level++) { var unitsPerPixel = unitsWidth / (pixelsWidth * Math.Pow(2, level)); var tileSetElement = doc.CreateElement(Identifiers.TileSetElement); var href = $"{this.baseUrl}/{Identifiers.Tms}/{Identifiers.Version100}/{layer.Identifier}/{level}"; var hrefAttribute = doc.CreateAttribute(Identifiers.HRefAttribute); hrefAttribute.Value = href; tileSetElement.Attributes.Append(hrefAttribute); var unitsPerPixelAttribute = doc.CreateAttribute(Identifiers.UnitsPerPixelAttribute); unitsPerPixelAttribute.Value = unitsPerPixel.ToString(CultureInfo.InvariantCulture); tileSetElement.Attributes.Append(unitsPerPixelAttribute); var orderAttribute = doc.CreateAttribute("order"); orderAttribute.Value = level.ToString(CultureInfo.InvariantCulture); tileSetElement.Attributes.Append(orderAttribute); tileSetsElement.AppendChild(tileSetElement); } return(doc); }
public XmlDocument GetTileSets(Models.Layer layer) { var doc = new XmlDocument(); var root = doc.CreateElement(String.Empty, "TileMap", String.Empty); doc.AppendChild(root); var versionAttribute = doc.CreateAttribute("version"); versionAttribute.Value = TileMapServiceVersion; root.Attributes.Append(versionAttribute); var tilemapservice = $"{this.baseUrl}/tms/{TileMapServiceVersion}/"; var tilemapserviceAttribute = doc.CreateAttribute("tilemapservice"); tilemapserviceAttribute.Value = tilemapservice; root.Attributes.Append(tilemapserviceAttribute); var titleNode = doc.CreateElement("Title"); titleNode.AppendChild(doc.CreateTextNode(layer.Title)); root.AppendChild(titleNode); var srs = doc.CreateElement("SRS"); srs.AppendChild(doc.CreateTextNode(EPSG3857)); root.AppendChild(srs); // GoogleMapsCompatible tile grid const double MinX = -20037508.342789; const double MinY = -20037508.342789; const double MaxX = 20037508.342789; const double MaxY = 20037508.342789; var boundingBox = doc.CreateElement("BoundingBox"); var minxAttribute = doc.CreateAttribute("minx"); minxAttribute.Value = MinX.ToString("F6", CultureInfo.InvariantCulture); boundingBox.Attributes.Append(minxAttribute); var minyAttribute = doc.CreateAttribute("miny"); minyAttribute.Value = MinY.ToString("F6", CultureInfo.InvariantCulture); boundingBox.Attributes.Append(minyAttribute); var maxxAttribute = doc.CreateAttribute("maxx"); maxxAttribute.Value = MaxX.ToString("F6", CultureInfo.InvariantCulture); boundingBox.Attributes.Append(maxxAttribute); var maxyAttribute = doc.CreateAttribute("maxy"); maxyAttribute.Value = MaxY.ToString("F6", CultureInfo.InvariantCulture); boundingBox.Attributes.Append(maxyAttribute); root.AppendChild(boundingBox); var origin = doc.CreateElement("Origin"); var yAttribute = doc.CreateAttribute("y"); yAttribute.Value = MinY.ToString("F6", CultureInfo.InvariantCulture); origin.Attributes.Append(yAttribute); var xAttribute = doc.CreateAttribute("x"); xAttribute.Value = MinX.ToString("F6", CultureInfo.InvariantCulture); origin.Attributes.Append(xAttribute); root.AppendChild(origin); var tileFormat = doc.CreateElement("TileFormat"); var extensionAttribute = doc.CreateAttribute("extension"); extensionAttribute.Value = layer.Format; // TODO: jpg/jpeg ? tileFormat.Attributes.Append(extensionAttribute); var mimetypeAttribute = doc.CreateAttribute("mime-type"); mimetypeAttribute.Value = layer.ContentType; tileFormat.Attributes.Append(mimetypeAttribute); var heightAttribute = doc.CreateAttribute("height"); heightAttribute.Value = TileHeight.ToString(CultureInfo.InvariantCulture); tileFormat.Attributes.Append(heightAttribute); var widthAttribute = doc.CreateAttribute("width"); widthAttribute.Value = TileWidth.ToString(CultureInfo.InvariantCulture); tileFormat.Attributes.Append(widthAttribute); root.AppendChild(tileFormat); var tileSets = doc.CreateElement("TileSets"); root.AppendChild(tileSets); for (var level = layer.MinZoom; level <= layer.MaxZoom; level++) { var tileSet = doc.CreateElement("TileSet"); var href = $"{this.baseUrl}/tms/{TileMapServiceVersion}/{layer.Identifier}/{level}"; var hrefAttribute = doc.CreateAttribute("href"); hrefAttribute.Value = href; tileSet.Attributes.Append(hrefAttribute); var orderAttribute = doc.CreateAttribute("order"); orderAttribute.Value = $"{level}"; tileSet.Attributes.Append(orderAttribute); // TODO: ? units-per-pixel = 78271.516 / 2^n // TODO: ? an initial zoom level that consists of four 256x256 pixel tiles covering the whole earth var unitsperpixel = (MaxX - MinX) / (((double)TileWidth) * Math.Pow(2, level)); var unitsperpixelAttribute = doc.CreateAttribute("units-per-pixel"); unitsperpixelAttribute.Value = unitsperpixel.ToString(CultureInfo.InvariantCulture); tileSet.Attributes.Append(unitsperpixelAttribute); tileSets.AppendChild(tileSet); } return(doc); }
private async Task <Models.Layer?> GetSourceCapabilitiesAsync() { if (this.client == null) { throw new InvalidOperationException("HTTP client was not initialized."); } if (String.IsNullOrEmpty(this.configuration.Type)) { throw new InvalidOperationException("configuration.Type is null or empty"); } if (String.IsNullOrEmpty(this.configuration.Location)) { throw new InvalidOperationException("configuration.Location is null or empty"); } var result = new Models.Layer { GeographicalBounds = null, TileWidth = Utils.WebMercator.DefaultTileWidth, TileHeight = Utils.WebMercator.DefaultTileHeight, }; // TODO: separate WMS/WMTS/TMS capabilities client classes var sourceType = this.configuration.Type.ToLowerInvariant(); switch (sourceType) { case SourceConfiguration.TypeTms: { var r = await this.client.GetAsync(this.configuration.Location); if (r.IsSuccessStatusCode) { var xml = await r.Content.ReadAsStringAsync(); if (!String.IsNullOrEmpty(xml)) { var doc = new XmlDocument(); doc.LoadXml(xml); var properties = Tms.CapabilitiesUtility.ParseTileMap(doc); result.TileWidth = properties.TileWidth; result.TileHeight = properties.TileHeight; } } break; } case SourceConfiguration.TypeWmts: { string?sourceLayerIdentifier = null, capabilitiesUrl = null; if (this.configuration.Wmts != null) { capabilitiesUrl = (!String.IsNullOrWhiteSpace(this.configuration.Wmts.CapabilitiesUrl) && this.configuration.Wmts.CapabilitiesUrl.EndsWith(".xml")) ? this.configuration.Wmts.CapabilitiesUrl : Wmts.QueryUtility.GetCapabilitiesKvpUrl(this.configuration.Location); sourceLayerIdentifier = this.configuration.Wmts.Layer; } else { capabilitiesUrl = Wmts.QueryUtility.GetCapabilitiesKvpUrl(this.configuration.Location); sourceLayerIdentifier = Utils.UrlHelper.GetQueryParameters(this.configuration.Location) .First(p => String.Compare(p.Key, Wmts.QueryUtility.WmtsQueryLayer, StringComparison.OrdinalIgnoreCase) == 0) .Value; } var r = await this.client.GetAsync(capabilitiesUrl); if (r.IsSuccessStatusCode) { var xml = await r.Content.ReadAsStringAsync(); if (!String.IsNullOrEmpty(xml)) { var doc = new XmlDocument(); doc.LoadXml(xml); var layers = Wmts.CapabilitiesUtility.GetLayers(doc); var layer = layers.FirstOrDefault(l => l.Identifier == sourceLayerIdentifier); if (layer != null) { result.GeographicalBounds = layer.GeographicalBounds; } // TODO: detect KVP/RESTful syntax support for GetTile from source capabilities // TODO: use ResourceURL resourceType="tile" template="...", change Location ? // TODO: ContentType/Format } } break; } case SourceConfiguration.TypeWms: { string?sourceLayerIdentifier = null; if (this.configuration.Wms != null) { sourceLayerIdentifier = this.configuration.Wms.Layer; } else { sourceLayerIdentifier = Utils.UrlHelper.GetQueryParameters(this.configuration.Location) .First(p => String.Compare(p.Key, Wms.QueryUtility.WmsQueryLayers, StringComparison.OrdinalIgnoreCase) == 0) .Value; } // TODO: cache XML Capabilities documents from same source var url = Wms.QueryUtility.GetCapabilitiesUrl(this.configuration); var r = await this.client.GetAsync(url); if (r.IsSuccessStatusCode) { var xml = await r.Content.ReadAsStringAsync(); if (!String.IsNullOrEmpty(xml)) { var doc = new XmlDocument(); doc.LoadXml(xml); var layers = Wms.CapabilitiesUtility.GetLayers(doc); var layer = layers.FirstOrDefault(l => l.Name == sourceLayerIdentifier); if (layer != null) { result.GeographicalBounds = layer.GeographicalBounds; } // TODO: ContentType/Format } } break; } } return(result); }