Direction Marker

Mar 5, 2010 at 2:51 PM

Hi,

How can I do direction marker in GMap.net?

Thanks.

Ramazan ULUCAY

Coordinator
Mar 5, 2010 at 3:40 PM

direction, you mean circle with arrow on renderTransform at some angle?

Mar 6, 2010 at 8:56 AM
Edited Mar 6, 2010 at 9:00 AM

Yes. I want to show the directions with angle for arrow. 

Coordinator
Mar 6, 2010 at 2:19 PM

try this:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
using GMap.NET.WindowsPresentation;
using Trolis.Entities;
using Trolis.LinqToSql;
using System.Diagnostics;
using System.Globalization;

namespace Trolis.Markers
{
   public class DrawingVisualFx : DrawingVisual
   {
      public static readonly DependencyProperty EffectProperty = DependencyProperty.Register("Effect", typeof(Effect), typeof(DrawingVisualFx),
                                      new FrameworkPropertyMetadata(null, (FrameworkPropertyMetadataOptions.AffectsRender), new PropertyChangedCallback(OnEffectChanged)));
      public Effect Effect
      {
         get
         {
            return (Effect) GetValue(EffectProperty);
         }
         set
         {
            SetValue(EffectProperty, value);
         }
      }

      private static void OnEffectChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
      {
         DrawingVisualFx drawingVisualFx = o as DrawingVisualFx;
         if(drawingVisualFx != null)
         {
            drawingVisualFx.setMyProtectedVisualEffect((Effect) e.NewValue);
         }
      }

      private void setMyProtectedVisualEffect(Effect effect)
      {
         VisualEffect = effect;
      }
   }

   public class CircleVisual : FrameworkElement
   {
      Popup Popup = new Popup();
      public readonly Controls.TrolleyTooltip Tooltip = new Trolis.Controls.TrolleyTooltip();
      public readonly TrolleyMarker Marker;

      public CircleVisual(TrolleyMarker m)
      {
         Marker = m;
         Popup.AllowsTransparency = true;
         Popup.PlacementTarget = this;
         Popup.Placement = PlacementMode.Mouse;
         Popup.Child = Tooltip;
         Popup.Child.Opacity = 0.777;

         Marker.ZIndex = 100;

         SizeChanged += new SizeChangedEventHandler(CircleVisual_SizeChanged);
         MouseEnter += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseEnter);
         MouseLeave += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseLeave);
         MouseUp += new MouseButtonEventHandler(CircleVisual_MouseUp);
         Loaded += new RoutedEventHandler(OnLoaded);

         Text = "?";

         StrokeArrow.EndLineCap = PenLineCap.Triangle;
         StrokeArrow.LineJoin = PenLineJoin.Round;

         RenderTransform = scale;

         Width = Height = 22;
         FontSize = (Width/1.55);
         Stroke.Thickness = Width/22;
      }

      void CircleVisual_MouseUp(object sender, MouseButtonEventArgs e)
      {
         var vh = this.Marker.Data;
         if(vh != null && MainWindow.Instance.Company == Company.Trolley)
         {
            MainWindow.Instance.SetCurrentTrolleyMarker(vh);
         }
      }

      void CircleVisual_SizeChanged(object sender, SizeChangedEventArgs e)
      {
         Marker.Offset = new System.Windows.Point(-e.NewSize.Width/2, -e.NewSize.Height/2);
         scale.CenterX = -Marker.Offset.X;
         scale.CenterY = -Marker.Offset.Y;
      }

      void OnLoaded(object sender, RoutedEventArgs e)
      {
         UpdateVisual(true);
      }

      ScaleTransform scale = new ScaleTransform(1, 1);

      void CircleVisual_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
      {
         Stroke.Thickness -= 2;
         if(Popup.IsOpen)
         {
            Popup.IsOpen = false;
         }

         Marker.ZIndex -= 10000;
         Cursor = Cursors.Arrow;

         this.Effect = null;

         scale.ScaleY = 1;
         scale.ScaleX = 1;
      }

      void CircleVisual_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
      {
         Stroke.Thickness += 2;
         if(!Popup.IsOpen)
         {
            Popup.IsOpen = true;
         }

         Marker.ZIndex += 10000;
         Cursor = Cursors.Hand;

         this.Effect = ShadowEffect;

         scale.ScaleY = 1.5;
         scale.ScaleX = 1.5;
      }

      public DropShadowEffect ShadowEffect;

      static Typeface Font = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);
      FormattedText FText;

      private Brush background = Brushes.Blue;
      public Brush Background
      {
         get
         {
            return background;
         }
         set
         {
            if(background != value)
            {
               background = value;
               IsChanged = true;
            }
         }
      }

      private Brush foreground = Brushes.White;
      public Brush Foreground
      {
         get
         {
            return foreground;
         }
         set
         {
            if(foreground != value)
            {
               foreground = value;
               IsChanged = true;

               ForceUpdateText();
            }
         }
      }

      private Pen stroke = new Pen(Brushes.Blue, 10.0);
      public Pen Stroke
      {
         get
         {
            return stroke;
         }
         set
         {
            if(stroke != value)
            {
               stroke = value;
               IsChanged = true;
            }
         }
      }

      private Pen strokeArrow = new Pen(Brushes.Blue, 2.0);
      public Pen StrokeArrow
      {
         get
         {
            return strokeArrow;
         }
         set
         {
            if(strokeArrow != value)
            {
               strokeArrow = value;
               IsChanged = true;
            }
         }
      }

      public double FontSize = 16;

      private double? angle = 0;
      public double? Angle
      {
         get
         {
            return angle;
         }
         set
         {
            if(!Angle.HasValue || !value.HasValue || (Angle.HasValue && value.HasValue && Math.Abs(angle.Value - value.Value) > 11))
            {
               angle = value;
               IsChanged = true;
            }
         }
      }
      public bool IsChanged = true;

      void ForceUpdateText()
      {
         FText = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, Font, FontSize, Foreground);
         IsChanged = true;
      }

      string text;
      public string Text
      {
         get
         {
            return text;
         }
         set
         {
            if(text != value)
            {
               text = value;
               ForceUpdateText();
            }
         }
      }

      Visual _child;
      public virtual Visual Child
      {
         get
         {
            return _child;
         }
         set
         {
            if(_child != value)
            {
               if(_child != null)
               {
                  RemoveLogicalChild(_child);
                  RemoveVisualChild(_child);
               }

               if(value != null)
               {
                  AddVisualChild(value);
                  AddLogicalChild(value);
               }

               // cache the new child
               _child = value;

               InvalidateVisual();
            }
         }
      }

      public bool UpdateVisual(bool forceUpdate)
      {
         if(forceUpdate || IsChanged)
         {
            Child = Create();
            IsChanged = false;
            return true;
         }

         return false;
      }

      int countCreate = 0;

      private DrawingVisual Create()
      {
         countCreate++;

         var square = new DrawingVisualFx();

         using(DrawingContext dc = square.RenderOpen())
         {
            dc.DrawEllipse(null, Stroke, new Point(Width/2, Height/2), Width/2 + Stroke.Thickness/2, Height/2 + Stroke.Thickness/2);

            //dc.DrawLine(StrokeArrow, new Point(-Width*0.5, Height*0.5), new Point(Width*1.5, Height*0.5));
            //dc.DrawLine(StrokeArrow, new Point(Width*0.5, -Height*0.5), new Point(Width*0.5, Height*1.5));

            if(Angle.HasValue)
            {
               dc.PushTransform(new RotateTransform(Angle.Value, Width/2, Height/2));
               {
                  PolyLineSegment polySeg = new PolyLineSegment(new Point[] { new Point(Width*0.2, Height*0.3), new Point(Width*0.8, Height*0.3) }, true);
                  PathFigure pathFig = new PathFigure(new Point(Width*0.5, -Height*0.22), new PathSegment[] { polySeg }, true);
                  PathGeometry pathGeo = new PathGeometry(new PathFigure[] { pathFig });
                  dc.DrawGeometry(Brushes.AliceBlue, StrokeArrow, pathGeo);
               }
               dc.Pop();
            }

            dc.DrawEllipse(Background, null, new Point(Width/2, Height/2), Width/2, Height/2);
            dc.DrawText(FText, new Point(Width/2 - FText.Width/2, Height/2 - FText.Height/2));
         }

         return square;
      }

      #region Necessary Overrides -- Needed by WPF to maintain bookkeeping of our hosted visuals
      protected override int VisualChildrenCount
      {
         get
         {
            return (Child == null ? 0 : 1);
         }
      }

      protected override Visual GetVisualChild(int index)
      {
         return Child;
      }
      #endregion
   }
Mar 6, 2010 at 3:07 PM
Edited Mar 6, 2010 at 3:09 PM

Pardon me radioman. I write Windows Application. And I have solved now.

 

namespace GMap.NET.WindowsForms.Markers
{
    using System.Drawing;
    using System.Windows.Forms.Properties;
    using System;
    public class GMapMarkerDirection : GMapMarker
    {
        float Ang;
        public GMapMarkerDirection(PointLatLng p,float angle)
            : base(p)
        {
            Ang = angle;
        }

        public override void OnRender(Graphics g)
        {

            g.DrawImageUnscaled(RotateImage(Resources.arrow, Ang), LocalPosition.X - Resources.arrow.Width / 2, LocalPosition.Y - Resources.arrow.Height / 2);
        }

        //http://www.codeproject.com/KB/graphics/rotateimage.aspx
        //Author : James T. Johnson
        private static Bitmap RotateImage(Image image, float angle)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            const double pi2 = Math.PI / 2.0;

            // Why can't C# allow these to be const, or at least readonly
            // *sigh*  I'm starting to talk like Christian Graus :omg:
            double oldWidth = (double)image.Width;
            double oldHeight = (double)image.Height;

            // Convert degrees to radians
            double theta = ((double)angle) * Math.PI / 180.0;
            double locked_theta = theta;

            // Ensure theta is now [0, 2pi)
            while (locked_theta < 0.0)
                locked_theta += 2 * Math.PI;

            double newWidth, newHeight;
            int nWidth, nHeight; // The newWidth/newHeight expressed as ints

            #region Explaination of the calculations
            /*
			 * The trig involved in calculating the new width and height
			 * is fairly simple; the hard part was remembering that when 
			 * PI/2 <= theta <= PI and 3PI/2 <= theta < 2PI the width and 
			 * height are switched.
			 * 
			 * When you rotate a rectangle, r, the bounding box surrounding r
			 * contains for right-triangles of empty space.  Each of the 
			 * triangles hypotenuse's are a known length, either the width or
			 * the height of r.  Because we know the length of the hypotenuse
			 * and we have a known angle of rotation, we can use the trig
			 * function identities to find the length of the other two sides.
			 * 
			 * sine = opposite/hypotenuse
			 * cosine = adjacent/hypotenuse
			 * 
			 * solving for the unknown we get
			 * 
			 * opposite = sine * hypotenuse
			 * adjacent = cosine * hypotenuse
			 * 
			 * Another interesting point about these triangles is that there
			 * are only two different triangles. The proof for which is easy
			 * to see, but its been too long since I've written a proof that
			 * I can't explain it well enough to want to publish it.  
			 * 
			 * Just trust me when I say the triangles formed by the lengths 
			 * width are always the same (for a given theta) and the same 
			 * goes for the height of r.
			 * 
			 * Rather than associate the opposite/adjacent sides with the
			 * width and height of the original bitmap, I'll associate them
			 * based on their position.
			 * 
			 * adjacent/oppositeTop will refer to the triangles making up the 
			 * upper right and lower left corners
			 * 
			 * adjacent/oppositeBottom will refer to the triangles making up 
			 * the upper left and lower right corners
			 * 
			 * The names are based on the right side corners, because thats 
			 * where I did my work on paper (the right side).
			 * 
			 * Now if you draw this out, you will see that the width of the 
			 * bounding box is calculated by adding together adjacentTop and 
			 * oppositeBottom while the height is calculate by adding 
			 * together adjacentBottom and oppositeTop.
			 */
            #endregion

            double adjacentTop, oppositeTop;
            double adjacentBottom, oppositeBottom;

            // We need to calculate the sides of the triangles based
            // on how much rotation is being done to the bitmap.
            //   Refer to the first paragraph in the explaination above for 
            //   reasons why.
            if ((locked_theta >= 0.0 && locked_theta < pi2) ||
                (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2)))
            {
                adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
                oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth;

                adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
                oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
            }
            else
            {
                adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
                oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight;

                adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
                oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
            }

            newWidth = adjacentTop + oppositeBottom;
            newHeight = adjacentBottom + oppositeTop;

            nWidth = (int)Math.Ceiling(newWidth);
            nHeight = (int)Math.Ceiling(newHeight);

            Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);

            using (Graphics g = Graphics.FromImage(rotatedBmp))
            {
                // This array will be used to pass in the three points that 
                // make up the rotated image
                Point[] points;

                /*
                 * The values of opposite/adjacentTop/Bottom are referring to 
                 * fixed locations instead of in relation to the
                 * rotating image so I need to change which values are used
                 * based on the how much the image is rotating.
                 * 
                 * For each point, one of the coordinates will always be 0, 
                 * nWidth, or nHeight.  This because the Bitmap we are drawing on
                 * is the bounding box for the rotated bitmap.  If both of the 
                 * corrdinates for any of the given points wasn't in the set above
                 * then the bitmap we are drawing on WOULDN'T be the bounding box
                 * as required.
                 */
                if (locked_theta >= 0.0 && locked_theta < pi2)
                {
                    points = new Point[] { 
											 new Point( (int) oppositeBottom, 0 ), 
											 new Point( nWidth, (int) oppositeTop ),
											 new Point( 0, (int) adjacentBottom )
										 };

                }
                else if (locked_theta >= pi2 && locked_theta < Math.PI)
                {
                    points = new Point[] { 
											 new Point( nWidth, (int) oppositeTop ),
											 new Point( (int) adjacentTop, nHeight ),
											 new Point( (int) oppositeBottom, 0 )						 
										 };
                }
                else if (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))
                {
                    points = new Point[] { 
											 new Point( (int) adjacentTop, nHeight ), 
											 new Point( 0, (int) adjacentBottom ),
											 new Point( nWidth, (int) oppositeTop )
										 };
                }
                else
                {
                    points = new Point[] { 
											 new Point( 0, (int) adjacentBottom ), 
											 new Point( (int) oppositeBottom, 0 ),
											 new Point( (int) adjacentTop, nHeight )		
										 };
                }

                g.DrawImage(image, points);
            }

            return rotatedBmp;
        }

    }
}

 

 

Coordinator
Mar 6, 2010 at 7:43 PM

ohh you using windows forms, actually you can push transformation:

g.RotateTransform(15);

g.Draw(...);

g.ResetTransform();

Mar 6, 2010 at 9:23 PM

I've tried it and it's runing but image's changed. Markers shows half.

Coordinator
Mar 6, 2010 at 10:52 PM

you can only paint transformed arrow not all image