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);
        }
예제 #2
0
        /// <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);
        }
예제 #4
0
        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);
        }