Ejemplo n.º 1
0
        /// <summary>
        /// Creates the transformation function that determines the position of a
        /// source pixel, given the position of a target pixel.
        /// </summary>
        /// <param name="targetMapRectangle">The requested target rectangle.</param>
        /// <param name="targetSize">The requested target size.</param>
        /// <param name="sourceBoundingBox">The source bounding box corresponding to the target map rectangle..</param>
        /// <param name="sourceSize">The source size corresponding to the target size.</param>
        /// <returns>Position of source pixel</returns>
        protected virtual Func <PointD, PointD> GetTransformFunction(MapRectangle targetMapRectangle, Size targetSize, IBoundingBox sourceBoundingBox, Size sourceSize)
        {
            return(target =>
            {
                // knowing the map rectangle, we can compute the logical coordinate corresponding to the position of the target pixel
                var pLogicalTarget = new Location(
                    targetMapRectangle.Left + (targetMapRectangle.Right - targetMapRectangle.Left) * (target.X / (targetSize.Width - 1)),
                    targetMapRectangle.Top - (targetMapRectangle.Top - targetMapRectangle.Bottom) * (target.Y / (targetSize.Height - 1))
                    );

                // transform this logical coordinate into the source CRS
                var pLogicalSource = TargetToSourceTransformation.Transform(pLogicalTarget);

                // knowing the source bounding box and its configured orientation, we can now
                // turn the logical source coordinate into the logical offsets (left and top)
                var sourceOffset = new SizeD(

                    new[] { ContentAlignment.TopLeft, ContentAlignment.BottomLeft }.Contains(SourceMapService.MinAlignment)
                        ? pLogicalSource.X - sourceBoundingBox.MinX
                        : sourceBoundingBox.MaxX - pLogicalSource.X,

                    new[] { ContentAlignment.BottomLeft, ContentAlignment.BottomRight }.Contains(SourceMapService.MinAlignment)
                        ? sourceBoundingBox.MaxY - pLogicalSource.Y
                        : pLogicalSource.Y - sourceBoundingBox.MinY
                    );

                // and finally, we an turn the logical offsets into the pixel position
                return new PointD(
                    sourceSize.Width * sourceOffset.Width / sourceBoundingBox.Size().Width,
                    sourceSize.Height * sourceOffset.Height / sourceBoundingBox.Size().Height
                    );
            });
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Calculates a zoom factor given the logical bounds and the pixel size of a map section.
 /// </summary>
 /// <param name="boundingBox">Logical bounds of the map section.</param>
 /// <param name="pixelSize">Pixel size of the map section.</param>
 /// <returns>Zoom factor</returns>
 private static double CalculateZoom(IBoundingBox boundingBox, Size pixelSize)
 {
     return(CalculateZoom(boundingBox.Size(), pixelSize));
 }
Ejemplo n.º 3
0
        /// <inheritdoc/>
        public override Stream GetImageStream(IBoundingBox boundingBox, Size requestedSize, out Size effectiveSize)
        {
            // must initialize effective size ... assuming we're returning the requested size.
            effectiveSize = requestedSize;

            // see ParamsValid for explanation;
            // if parameters are invalid, we simply return null (= no image available)
            if (!ParamsValid(boundingBox, requestedSize))
            {
                return(null);
            }

            // find the matrix set that is closest to the given bounds and size
            var matrixSet = SelectTileMatrix(boundingBox, requestedSize);

            // if no matrix set has been found, we simply return null (= no image available)
            if (matrixSet == null)
            {
                return(null);
            }

            // determine the logical width & height of a single tile
            var logicalTileWidth  = (matrixSet.BottomRightCorner.X - matrixSet.TopLeftCorner.X) / matrixSet.MatrixWidth;
            var logicalTileHeight = (matrixSet.TopLeftCorner.Y - matrixSet.BottomRightCorner.Y) / matrixSet.MatrixHeight;

            // determine the top left tile that is required to cover the requested bounding box
            var tileLeft = (int)Math.Floor((boundingBox.MinX - matrixSet.TopLeftCorner.X) / logicalTileWidth);
            var tileTop  = (int)Math.Floor((matrixSet.TopLeftCorner.Y - boundingBox.MaxY) / logicalTileHeight);

            // determine the lower right tile that is required to cover the requested bounding box
            var tileRight  = (int)Math.Ceiling((boundingBox.MaxX - matrixSet.TopLeftCorner.X) / logicalTileWidth) - 1;
            var tileBottom = (int)Math.Ceiling((matrixSet.TopLeftCorner.Y - boundingBox.MinY) / logicalTileHeight) - 1;

            // apply limits as defined by the selected matrix set
            tileLeft   = Math.Max(tileLeft, matrixSet.MinX());
            tileTop    = Math.Max(tileTop, matrixSet.MinY());
            tileRight  = Math.Min(tileRight, matrixSet.MaxX());
            tileBottom = Math.Min(tileBottom, matrixSet.MaxY());

            try
            {
                // resulting image
                Image resultingImage = null;

                // check if there is anything to be rendered
                if (tileRight < tileLeft || tileBottom < tileTop)
                {
                    // there are no tile to be rendered; return empty image
                    resultingImage = CreateBitmap(requestedSize.Width, requestedSize.Height, true);
                }
                else
                {
                    try
                    {
                        // create a temporary image in which we place the tiles
                        var tilesImage = CreateBitmap((tileRight - tileLeft + 1) * matrixSet.TileWidth, (tileBottom - tileTop + 1) * matrixSet.TileHeight);

                        // fail, if image is invalid. This happens e.g. due to an invalid image size.
                        if (tilesImage == null)
                        {
                            return(null);
                        }

                        using (var tileGraphics = Graphics.FromImage(tilesImage))
                        {
                            // loop through the tiles, request and draw the images
                            // we'll pass on WebExceptions but we will swallow any other exception

                            for (int tx = tileLeft, ox = 0; tx <= tileRight; ++tx, ox += matrixSet.TileWidth)
                            {
                                for (int ty = tileTop, oy = 0; ty <= tileBottom; ++ty, oy += matrixSet.TileHeight)
                                {
                                    try
                                    {
                                        var image = Template.ToLowerInvariant() == "test"
                                            ? RenderTestImage(matrixSet.TileWidth, matrixSet.TileHeight, 32, tx, ty)
                                            : LoadImage(matrixSet.Identifier, tx, ty);

                                        tileGraphics.DrawImageUnscaled(image, new Point(ox, oy));
                                    }

                                    // pass on WebException, swallow others

                                    catch (WebException) { throw; }
                                }
                            }
                            catch { /* ignored */ }
                        }

                        // determine the logical bounds covered by map rendered above
                        var logicalTileRect = new Tools.Reprojection.MapRectangle(
                            matrixSet.TopLeftCorner.X + tileLeft * logicalTileWidth,
                            matrixSet.TopLeftCorner.Y - tileTop * logicalTileHeight,
                            matrixSet.TopLeftCorner.X + (tileRight + 1) * logicalTileHeight,
                            matrixSet.TopLeftCorner.Y - (tileBottom + 1) * logicalTileHeight
                            );


                        // test, if the tile image fully covers the bounding box that has initially been requested.
                        //
                        // if that is the case, we'll extract the requested bounding box from that image (unscaled) and
                        // return that. This avoids an additional drawing operation that scales the image and decreases quality.

                        if (boundingBox.MinX >= logicalTileRect.MinX && boundingBox.MaxX <= logicalTileRect.MaxX && boundingBox.MinY >= logicalTileRect.MinX && boundingBox.MaxY <= logicalTileRect.MaxY)
                        {
                            // the tile image fully covers the bounding box that has initially been requested.

                            // determine position and extent of the originally requested bounding box in the tile image
                            var x = (int)Math.Round(tilesImage.Width * (boundingBox.MinX - logicalTileRect.MinX) / logicalTileRect.Size().Width);
                            var y = (int)Math.Round(tilesImage.Height * (logicalTileRect.MaxY - boundingBox.MaxY) / logicalTileRect.Size().Height);

                            var w = (int)Math.Round((double)tilesImage.Width * boundingBox.Size().Width / logicalTileRect.Size().Width);
                            var h = (int)Math.Round((double)tilesImage.Height * boundingBox.Size().Height / logicalTileRect.Size().Height);

                            // the following code "extracts" the originally requested bounding box into the result image
                            // since we're not going to return the requestedSize, we must set effectiveSize accordingly.

                            effectiveSize  = new Size(w, h);
                            resultingImage = CreateBitmap(w, h);

                            using (var g = Graphics.FromImage(resultingImage))
                                g.DrawImageUnscaled(tilesImage, -x, -y);
                        }
                        else
                        {
                            resultingImage = CreateBitmap(requestedSize.Width, requestedSize.Height, true);

                            using (var graphics = Graphics.FromImage(resultingImage))
                            {
                                // calculate position and size for placing the tile image into
                                // the resulting image that was initially requested

                                var zoomX = boundingBox.Size().Width / requestedSize.Width;
                                var zoomY = boundingBox.Size().Height / requestedSize.Height;

                                var dstRect = new Rectangle(
                                    // x & y
                                    (int)Math.Round((logicalTileRect.Left - boundingBox.MinX) / zoomX),
                                    (int)Math.Round((boundingBox.MaxY - logicalTileRect.Top) / zoomY),

                                    // width & height
                                    (int)Math.Round((tileRight - tileLeft + 1) * logicalTileWidth / zoomX),
                                    (int)Math.Round((tileBottom - tileTop + 1) * logicalTileHeight / zoomY)
                                    );

                                // draw the tile image using a high quality mode

                                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                                graphics.DrawImage(tilesImage, dstRect, 0, 0, tilesImage.Size.Width, tilesImage.Size.Height, GraphicsUnit.Pixel);
                            }
                        }
                    }

                    // pass on WebException, swallow others

                    catch (WebException) { throw; }
                    catch { /* ignored */ }
                }