Esempio n. 1
0
        private void DrawWaterMark(ImageCreationContext context)
        {
            if (context.Width < 200)
            {
                // too small...
                return;
            }
            var text            = "IsraelHikingMap.osm.org.il";
            var initialFontSize = 16;
            var fontFamiliyName = "Arial";

            using (Graphics graphics = Graphics.FromImage(context.Image))
                using (var fill = new SolidBrush(Color.FromArgb(128, 0, 0, 0)))
                    using (var outline = new Pen(Color.FromArgb(128, 255, 255, 255), 6))
                    {
                        Font stringFont     = new Font(fontFamiliyName, initialFontSize);
                        var  stringSize     = graphics.MeasureString(text, stringFont);
                        var  ratio          = (context.Width - 4) / stringSize.Width;
                        var  bottomPosition = context.Height - 2 - stringSize.Height * ratio;
                        var  fontSize       = initialFontSize * ratio;
                        stringFont = new Font(fontFamiliyName, fontSize);
                        // Draw string to screen.
                        GraphicsPath path = new GraphicsPath();
                        path.AddString(text,
                                       new FontFamily(fontFamiliyName),
                                       (int)FontStyle.Regular,
                                       graphics.DpiY * fontSize / 72, // em size,
                                       new PointF(2, bottomPosition),
                                       new StringFormat());
                        graphics.DrawPath(outline, path);
                        graphics.FillPath(fill, path);
                    }
        }
Esempio n. 2
0
        /// <summary>
        /// Allows conversion between the image pixels and wgs84 coordinates
        /// </summary>
        /// <param name="latLng"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private PointF ConvertLatLngToPoint(LatLng latLng, ImageCreationContext context)
        {
            var x = (float)((GetXTile(latLng.Lng, context.N) - context.TopLeft.X) * TILE_SIZE);
            var y = (float)((GetYTile(latLng.Lat, context.N) - context.TopLeft.Y) * TILE_SIZE);

            return(new PointF(x, y));
        }
Esempio n. 3
0
        /// <summary>
        /// Will create a single image from all the tiles - this image will include the required image inside
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task <Image> CreateSingleImageFromTiles(ImageCreationContext context)
        {
            var horizontalTiles = context.BottomRight.X - context.TopLeft.X + 1;
            var verticalTiles   = context.BottomRight.Y - context.TopLeft.Y + 1;
            var tasks           = new List <Task <ImageWithOffset> >();

            foreach (var addressTemplate in context.AddressesTemplates)
            {
                for (int x = 0; x < horizontalTiles; x++)
                {
                    for (int y = 0; y < verticalTiles; y++)
                    {
                        var task = GetTileImage(context.TopLeft, new Point(x, y), context.Zoom, addressTemplate);
                        tasks.Add(task);
                    }
                }
            }

            var imagesWithOffsets = await Task.WhenAll(tasks);

            var image = new Image <Rgba32>(horizontalTiles * TILE_SIZE, verticalTiles * TILE_SIZE);

            foreach (var imageWithOffset in imagesWithOffsets)
            {
                image.Mutate(x => x.DrawImage(imageWithOffset.Image,
                                              new Point(imageWithOffset.Offset.X * TILE_SIZE, imageWithOffset.Offset.Y * TILE_SIZE),
                                              1.0f));
            }
            return(image);
        }
Esempio n. 4
0
        /// <summary>
        /// This will update the address templates and remove empty addresses
        /// </summary>
        /// <param name="context"></param>
        private void UpdateAddressesTemplates(ImageCreationContext context)
        {
            var address = IsValidAddress(context.DataContainer.BaseLayer.Address)
                ? context.DataContainer.BaseLayer.Address
                : GetBaseAddressFromInvalidString(context.DataContainer.BaseLayer.Address);

            var addressTemplates = new List <AddressAndOpacity>
            {
                new AddressAndOpacity {
                    Address = FixAdrressTemplate(address), Opacity = context.DataContainer.BaseLayer.Opacity ?? 1.0
                }
            };

            foreach (var layerData in context.DataContainer.Overlays ?? new List <LayerData>())
            {
                if (IsValidAddress(layerData.Address) == false)
                {
                    continue;
                }
                var addressAndOpacity = new AddressAndOpacity
                {
                    Address = FixAdrressTemplate(layerData.Address),
                    Opacity = layerData.Opacity ?? 1.0
                };
                addressTemplates.Add(addressAndOpacity);
            }
            context.AddressesTemplates = addressTemplates.ToArray();
        }
Esempio n. 5
0
        /// <summary>
        /// Will create a single image from all the tiles - this image will include the required image inside
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task <Image> CreateSingleImageFromTiles(ImageCreationContext context)
        {
            var horizontalTiles = context.BottomRight.X - context.TopLeft.X + 1;
            var verticalTiles   = context.BottomRight.Y - context.TopLeft.Y + 1;
            var bitmap          = new Bitmap(horizontalTiles * TILE_SIZE, verticalTiles * TILE_SIZE);

            var tasks = new List <Task <ImageWithOffset> >();

            foreach (var addressTemplate in context.AddressesTemplates)
            {
                for (int x = 0; x < horizontalTiles; x++)
                {
                    for (int y = 0; y < verticalTiles; y++)
                    {
                        var task = GetTileImage(context.TopLeft, new Point(x, y), context.Zoom, addressTemplate);
                        tasks.Add(task);
                    }
                }
            }

            var imagesWithOffsets = await Task.WhenAll(tasks);

            using (var graphics = Graphics.FromImage(bitmap))
            {
                foreach (var imageWithOffset in imagesWithOffsets)
                {
                    graphics.DrawImage(imageWithOffset.Image,
                                       new Rectangle(imageWithOffset.Offset.X * TILE_SIZE, imageWithOffset.Offset.Y * TILE_SIZE, TILE_SIZE, TILE_SIZE),
                                       new Rectangle(0, 0, imageWithOffset.Image.Width, imageWithOffset.Image.Height),
                                       GraphicsUnit.Pixel);
                }
            }
            return(bitmap);
        }
Esempio n. 6
0
        /// <summary>
        /// This will update the address templates and remove empty addresses
        /// </summary>
        /// <param name="context"></param>
        private void UpdateAddressesTemplates(ImageCreationContext context)
        {
            var address = IsValidAddress(context.DataContainer.BaseLayer.Address) == false
                ? "https://israelhiking.osm.org.il/Hebrew/tiles/{z}/{x}/{y}.png"
                : context.DataContainer.BaseLayer.Address;

            var addressTemplates = new List <AddressAndOpacity>
            {
                new AddressAndOpacity {
                    Address = FixAdrressTemplate(address), Opacity = context.DataContainer.BaseLayer.Opacity ?? 1.0
                }
            };

            foreach (var layerData in context.DataContainer.Overlays ?? new List <LayerData>())
            {
                if (IsValidAddress(layerData.Address) == false)
                {
                    continue;
                }
                var addressAndOpacity = new AddressAndOpacity
                {
                    Address = FixAdrressTemplate(layerData.Address),
                    Opacity = layerData.Opacity ?? 1.0
                };
                addressTemplates.Add(addressAndOpacity);
            }
            context.AddressesTemplates = addressTemplates.ToArray();
        }
Esempio n. 7
0
        /// <summary>
        /// This will update the backgroud image - which is the image created from the baselayer and the overlay tiles
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task UpdateBackGroundImage(ImageCreationContext context)
        {
            var topLeft     = new Point((int)GetXTile(context.DataContainer.SouthWest.Lng, context.N), (int)GetYTile(context.DataContainer.NorthEast.Lat, context.N));
            var bottomRight = new Point((int)GetXTile(context.DataContainer.NorthEast.Lng, context.N), (int)GetYTile(context.DataContainer.SouthWest.Lat, context.N));

            context.TopLeft     = topLeft;
            context.BottomRight = bottomRight;
            context.Image       = await CreateSingleImageFromTiles(context);
        }
Esempio n. 8
0
        /// <summary>
        /// Crop and resizes the image to the desired dimentions
        /// </summary>
        /// <param name="context"></param>
        private void CropAndResizeImage(ImageCreationContext context)
        {
            var topLeft     = ConvertLatLngToPoint(context.DataContainer.SouthWest, context);
            var bottomRight = ConvertLatLngToPoint(context.DataContainer.NorthEast, context);

            context.Image.Mutate(x =>
                                 x.Crop(new Rectangle((int)topLeft.X, (int)bottomRight.Y, (int)(bottomRight.X - topLeft.X), (int)(topLeft.Y - bottomRight.Y)))
                                 .Resize(context.Width, context.Height)
                                 );
        }
Esempio n. 9
0
        /// <summary>
        /// Crop and resizes the image to the desired dimentions
        /// </summary>
        /// <param name="context"></param>
        private void CropAndResizeImage(ImageCreationContext context)
        {
            Bitmap bmp = new Bitmap(context.Width, context.Height);

            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                var topLeft     = ConvertLatLngToPoint(context.DataContainer.SouthWest, context);
                var bottomRight = ConvertLatLngToPoint(context.DataContainer.NorthEast, context);
                graphics.DrawImage(context.Image, new Rectangle(0, 0, bmp.Width, bmp.Height), topLeft.X, bottomRight.Y, bottomRight.X - topLeft.X, topLeft.Y - bottomRight.Y, GraphicsUnit.Pixel);
            }
            context.Image = bmp;
        }
Esempio n. 10
0
        /// <summary>
        /// This will draw on the backgroup image the route and markers according to color and opacity
        /// </summary>
        /// <param name="context"></param>
        private void DrawRoutesOnImage(ImageCreationContext context)
        {
            var penWidth           = PEN_WIDTH;
            var penWidthOffset     = 8;
            var circleOutlineWidth = 7f;
            var circleSize         = CIRCLE_SIZE;

            using (var graphics = Graphics.FromImage(context.Image))
                using (var outLinerPen = new Pen(Color.White, penWidth + penWidthOffset)
                {
                    LineJoin = LineJoin.Bevel
                })
                    using (var circleFillBrush = new SolidBrush(Color.White))
                        using (var startRoutePen = new Pen(Color.Green, circleOutlineWidth))
                            using (var endRoutePen = new Pen(Color.Red, circleOutlineWidth))
                            {
                                var routeColorIndex = 0;
                                foreach (var route in context.DataContainer.Routes)
                                {
                                    var points       = route.Segments.SelectMany(s => s.Latlngs).Select(l => ConvertLatLngToPoint(l, context)).ToArray();
                                    var markerPoints = route.Markers.Select(m => ConvertLatLngToPoint(m.Latlng, context));
                                    var lineColor    = _routeColors[routeColorIndex++];
                                    routeColorIndex = routeColorIndex % _routeColors.Length;
                                    if (!string.IsNullOrEmpty(route.Color))
                                    {
                                        lineColor = FromColorString(route.Color, route.Opacity);
                                    }

                                    using (var linePen = new Pen(lineColor, penWidth)
                                    {
                                        LineJoin = LineJoin.Bevel
                                    })
                                    {
                                        if (points.Any())
                                        {
                                            graphics.DrawLines(outLinerPen, points);
                                            graphics.DrawLines(linePen, points);

                                            graphics.FillEllipse(circleFillBrush, points.First().X - circleSize / 2, points.First().Y - circleSize / 2, circleSize, circleSize);
                                            graphics.DrawEllipse(startRoutePen, points.First().X - circleSize / 2, points.First().Y - circleSize / 2, circleSize, circleSize);
                                            graphics.FillEllipse(circleFillBrush, points.Last().X - circleSize / 2, points.Last().Y - circleSize / 2, circleSize, circleSize);
                                            graphics.DrawEllipse(endRoutePen, points.Last().X - circleSize / 2, points.Last().Y - circleSize / 2, circleSize, circleSize);
                                        }

                                        foreach (var markerPoint in markerPoints)
                                        {
                                            graphics.FillEllipse(circleFillBrush, markerPoint.X - circleSize / 2, markerPoint.Y - circleSize / 2, circleSize, circleSize);
                                            graphics.DrawEllipse(linePen, markerPoint.X - circleSize / 2, markerPoint.Y - circleSize / 2, circleSize, circleSize);
                                        }
                                    }
                                }
                            }
        }
Esempio n. 11
0
        /// <summary>
        /// Updates the zoom that will have tiles that have more pixels than the image needs
        /// This will make sure the image size will be bigger than the desired image to improve quility
        /// </summary>
        /// <param name="context"></param>
        private void UpdateZoom(ImageCreationContext context)
        {
            var    zoom = 0;
            double n;
            double deltaX;
            double deltaY;

            do
            {
                zoom++;
                n       = Math.Pow(2, zoom);
                deltaX  = GetXTile(context.DataContainer.NorthEast.Lng, n) - GetXTile(context.DataContainer.SouthWest.Lng, n);
                deltaY  = GetYTile(context.DataContainer.SouthWest.Lat, n) - GetYTile(context.DataContainer.NorthEast.Lat, n);
                deltaX *= TILE_SIZE;
                deltaY *= TILE_SIZE;
            } while (deltaX < context.Width && deltaY < context.Height);
            context.Zoom = zoom;
            context.N    = n;
        }
Esempio n. 12
0
        /// <summary>
        /// Updates the conrners of the datacontainer to fix the relevant image ratio
        /// It will increase the height or width of the container as needed.
        /// </summary>
        /// <param name="context"></param>
        private void UpdateCorners(ImageCreationContext context)
        {
            if (context.DataContainer.NorthEast == null || context.DataContainer.SouthWest == null)
            {
                var allLocations = context.DataContainer.Routes
                                   .SelectMany(r => r.Segments)
                                   .SelectMany(s => s.Latlngs)
                                   .Concat(context.DataContainer.Routes
                                           .SelectMany(r => r.Markers)
                                           .Select(m => m.Latlng)
                                           )
                                   .ToArray();
                context.DataContainer.NorthEast = new LatLng(allLocations.Max(l => l.Lat), allLocations.Max(l => l.Lng));
                context.DataContainer.SouthWest = new LatLng(allLocations.Min(l => l.Lat), allLocations.Min(l => l.Lng));
            }
            var n          = Math.Pow(2, MAX_ZOOM);
            var xNorthEast = GetXTile(context.DataContainer.NorthEast.Lng, n);
            var yNorthEast = GetYTile(context.DataContainer.NorthEast.Lat, n);
            var xSouthWest = GetXTile(context.DataContainer.SouthWest.Lng, n);
            var ySouthWest = GetYTile(context.DataContainer.SouthWest.Lat, n);
            var ratio      = context.Width * 1.0 / context.Height;

            if (xNorthEast - xSouthWest > ratio * (ySouthWest - yNorthEast))
            {
                var desiredY = (xNorthEast - xSouthWest) / ratio;
                var delatY   = (desiredY - (ySouthWest - yNorthEast)) / 2;
                ySouthWest += delatY;
                yNorthEast -= delatY;
            }
            else
            {
                var desiredX = (ySouthWest - yNorthEast) * ratio;
                var delatX   = (desiredX - (xNorthEast - xSouthWest)) / 2;
                xNorthEast += delatX;
                xSouthWest -= delatX;
            }

            context.DataContainer.NorthEast.Lng = GetLongitude(xNorthEast, n);
            context.DataContainer.NorthEast.Lat = GetLatitude(yNorthEast, n);
            context.DataContainer.SouthWest.Lng = GetLongitude(xSouthWest, n);
            context.DataContainer.SouthWest.Lat = GetLatitude(ySouthWest, n);
        }
Esempio n. 13
0
        ///<inheritdoc />
        public async Task <byte[]> Create(DataContainer dataContainer, int width, int height)
        {
            var context = new ImageCreationContext
            {
                Width         = width,
                Height        = height,
                DataContainer = dataContainer
            };

            UpdateCorners(context);
            UpdateZoom(context);
            UpdateAddressesTemplates(context);
            await UpdateBackGroundImage(context);

            DrawRoutesOnImage(context);
            CropAndResizeImage(context);
            var imageStream = new MemoryStream();

            context.Image.SaveAsPng(imageStream);
            return(imageStream.ToArray());
        }
Esempio n. 14
0
        /// <summary>
        /// This will draw on the backgroup image the route and markers according to color and opacity
        /// </summary>
        /// <param name="context"></param>
        private void DrawRoutesOnImage(ImageCreationContext context)
        {
            context.Image.Mutate(ctx =>
            {
                var routeColorIndex = 0;
                foreach (var route in context.DataContainer.Routes)
                {
                    var points       = route.Segments.SelectMany(s => s.Latlngs).Select(l => ConvertLatLngToPoint(l, context)).ToArray();
                    var markerPoints = route.Markers.Select(m => ConvertLatLngToPoint(m.Latlng, context));
                    var lineColor    = _routeColors[routeColorIndex++];
                    routeColorIndex  = routeColorIndex % _routeColors.Length;
                    if (!string.IsNullOrEmpty(route.Color))
                    {
                        lineColor = FromColorString(route.Color, route.Opacity);
                    }

                    if (points.Any())
                    {
                        var path = new SixLabors.Shapes.Path(new LinearLineSegment(points));
                        ctx.Draw(Color.White, PEN_WIDTH + PEN_WIDTH_OFFSET, path);
                        ctx.Draw(lineColor, PEN_WIDTH, path);
                        var startCircle = new EllipsePolygon(points.First(), CIRCLE_RADIUS);
                        ctx.Fill(Color.White, startCircle);
                        ctx.Draw(Color.Green, CIRCLE_OUTLINE_WIDTH, startCircle);
                        var endCircle = new EllipsePolygon(points.Last(), CIRCLE_RADIUS);
                        ctx.Fill(Color.White, endCircle);
                        ctx.Draw(Color.Red, CIRCLE_OUTLINE_WIDTH, endCircle);
                    }

                    foreach (var markerPoint in markerPoints)
                    {
                        var markerEllipse = new EllipsePolygon(markerPoint, CIRCLE_RADIUS);
                        ctx.Fill(Color.White, markerEllipse);
                        ctx.Draw(lineColor, PEN_WIDTH, markerEllipse);
                    }
                }
            });
        }
Esempio n. 15
0
        ///<inheritdoc />
        public async Task <byte[]> Create(DataContainer dataContainer, int width, int height)
        {
            _logger.LogDebug("Creating image for thumbnail started.");
            var context = new ImageCreationContext
            {
                Width         = width,
                Height        = height,
                DataContainer = dataContainer
            };

            UpdateCorners(context);
            UpdateZoom(context);
            UpdateAddressesTemplates(context);
            await UpdateBackGroundImage(context);

            DrawRoutesOnImage(context);
            CropAndResizeImage(context);
            var imageStream = new MemoryStream();

            context.Image.Save(imageStream, ImageFormat.Png);
            _logger.LogDebug("Creating image for thumbnail completed.");
            return(imageStream.ToArray());
        }