1. 程式人生 > >In me the tiger sniffs the rose

In me the tiger sniffs the rose

 基於winform的離線地圖專案

1.下載GMap(一個國外開源控制元件,codeplex有原始碼),不瞭解的百度一下,

2.專案引入GMap.NET.Core和GMap.NET.WindowsForms

3.工具箱新增GMap控制元件,把GMapControl控制元件拖到新建窗體

4.窗體載入事件初始化該控制元件的屬性或事件,OK!


一、載入地圖

private void Form1_Load(object sender, EventArgs e)
        {
            gmap.BackColor = Color.Red;

            //gmap.CacheLocation = Environment.CurrentDirectory + "\\GMapCache\\DataExp.gmdb"; //快取位置
            //設定控制元件的管理模式 (快取、聯網)
            //gmap.Manager.Mode = AccessMode.CacheOnly;
            gmap.Manager.Mode = AccessMode.ServerOnly;

            //設定控制元件顯示的地圖來源  
            //gmap.MapProvider = AMapSateliteProvider.Instance;
            //gmap.MapProvider = AMapProvider.Instance;
            //gmap.MapProvider = GMapProviders.GoogleChinaMap;
            gmap.MapProvider = AMapProvider.Instance;

            //載入離線快取
            GMaps.Instance.ImportFromGMDB(Application.StartupPath + "\\GMapCache\\DataExp.gmdb");


            //設定控制元件顯示的當前中心位置  
            // WGS84座標系,即地球座標系(通用座標系)
            // =》 經過國家測繪局要求採用GCJ02演算法加偏後即為“火星座標”(Google中國、騰訊和高德)
            // =》百度在“火星座標”的基礎上又採用了自己的加偏演算法BD09加偏後,得到“百度座標”(搜狗、圖吧都是)
            gmap.Position = new PointLatLng(30.606726, 114.424348);
            //gmap.Position = new PointLatLng(21.5053134635657, 67.8822576999664);//百度(BD09座標)

            gmap.MaxZoom = 16;
            gmap.MinZoom = 3;
            gmap.Zoom = 13;

            //不顯示中心十字點
            gmap.ShowCenter = false;

            //左鍵拖拽地圖
            gmap.DragButton = MouseButtons.Left;


            gmap.Overlays.Add(_markersOverlay);
            gmap.Overlays.Add(_polygons);
            gmap.Overlays.Add(objects);

            gmap.MouseClick += mapControl_MouseClick; //右鍵新增標記
            gmap.MouseDoubleClick += mapControl_DoubleClick; //右鍵新增標記

            gmap.OnMarkerClick += gmap_OnMarkerClick;
            gmap.OnMarkerClick2 += gmap_OnMarkerClick2;

        }
gmap.MapProvider = GMapProviders.GoogleChinaMap; 預設控制元件載入的地圖圖層是谷歌中國,要想載入國內的高德、百度等圖層,就要編寫一個圖層源的類。

高德:

AMapProviderBase

 public abstract class AMapProviderBase : GMapProvider
    {
        public AMapProviderBase()
        {
            MaxZoom = null;
            RefererUrl = "http://www.amap.com/";
            //Copyright = string.Format("©{0} 高德 Corporation, ©{0} NAVTEQ, ©{0} Image courtesy of NASA", DateTime.Today.Year);    
        }

        public override PureProjection Projection
        {
            get { return MercatorProjection.Instance; }
        }

        GMapProvider[] overlays;
        public override GMapProvider[] Overlays
        {
            get
            {
                if (overlays == null)
                {
                    overlays = new GMapProvider[] { this };
                }
                return overlays;
            }
        }
    }

AMapProvider
public class AMapProvider : AMapProviderBase
    {
        public static readonly AMapProvider Instance;

        readonly Guid id = new Guid("EF3DD303-3F74-4938-BF40-232D0595EE88");
        public override Guid Id
        {
            get { return id; }
        }

        readonly string name = "AMap";
        public override string Name
        {
            get
            {
                return name;
            }
        }

        static AMapProvider()
        {
            Instance = new AMapProvider();
        }

        public override PureImage GetTileImage(GPoint pos, int zoom)
        {
            try
            {
                string url = MakeTileImageUrl(pos, zoom, LanguageStr);
                return GetTileImageUsingHttp(url);
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        string MakeTileImageUrl(GPoint pos, int zoom, string language)
        {
            var num = (pos.X + pos.Y) % 4 + 1;
            //string url = string.Format(UrlFormat, num, pos.X, pos.Y, zoom);
            string url = string.Format(UrlFormat, pos.X, pos.Y, zoom);
            return url;
        }

        //static readonly string UrlFormat = "http://webrd04.is.autonavi.com/appmaptile?x={0}&y={1}&z={2}&lang=zh_cn&size=1&scale=1&style=7";
        //static readonly string UrlFormat = "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={0}&y={1}&z={2}";
        static readonly string UrlFormat = "http://localhost:8081/788865972/{2}/{0}/{1}.png"; //本地部署的GIS服務
    }

百度:

/// <summary>
    /// 百度地圖(BD09座標)
    /// 轉換:http://lbsyun.baidu.com/index.php?title=webapi/guide/changeposition
    /// </summary>
    public class BaiduMapProvider : BaiduMapProviderBase
    {
        readonly string name = "BaiduMap";
        public static readonly BaiduMapProvider Instance;
        readonly Guid id = new Guid("47C1561B-C785-4EBF-9EC3-2CF0E416E219");

        static BaiduMapProvider()
        {
            Instance = new BaiduMapProvider();
        }

        public override Guid Id
        {
            get { return id; }
        }

        public override string Name
        {
            get
            {
                return name;
            }
        }

        public override PureImage GetTileImage(GPoint pos, int zoom)
        {
            string url = MakeTileImageUrl(pos, zoom, LanguageStr);

            return GetTileImageUsingHttp(url);
        }

        string MakeTileImageUrl(GPoint pos, int zoom, string language)
        {
            zoom = zoom - 1;
            var offsetX = Math.Pow(2, zoom);
            var offsetY = offsetX - 1;

            var numX = pos.X - offsetX;
            var numY = -pos.Y + offsetY;

            zoom = zoom + 1;

            var num = (pos.X + pos.Y) % 8 + 1;
            var x = numX.ToString().Replace("-", "M");
            var y = numY.ToString().Replace("-", "M");

            //原來:http://q3.baidu.com/it/u=x=721;y=209;z=12;v=014;type=web&fm=44
            //更新:http://online1.map.bdimg.com/tile/?qt=tile&x=23144&y=6686&z=17&styles=pl
            //string url = string.Format(UrlFormat, num, x, y, zoom, "014", "web", "44"); 
            string url = string.Format(UrlFormat, x, y, zoom);
            return url;
        }

        //static readonly string UrlFormat = "http://q{0}.baidu.com/it/u=x={1};y={2};z={3};v={4};type={5}&fm={6}";
        static readonly string UrlFormat = "http://online1.map.bdimg.com/tile/?qt=tile&x={0}&y={1}&z={2}&styles=pl";

    }

引用:gmap.MapProvider = AMapProvider.Instance;


二、自定義Marker

 
[Serializable]
    public class GMapMarkerCircle : GMapMarker, ISerializable
    {
        /// <summary>
        /// 實際半徑距離,單位為米
        /// </summary>
        public double RawRadius;

        /// <summary>
        /// specifies how the outline is painted
        /// </summary>
        [NonSerialized]
        public Pen Stroke = new Pen(Color.FromArgb(155, Color.MidnightBlue));

        /// <summary>
        /// background color
        /// </summary>
        [NonSerialized]
        public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.AliceBlue));

        /// <summary>
        /// is filled
        /// </summary>
        public bool IsFilled = true;

        public GMapMarkerCircle(PointLatLng p, int radius)
            : base(p)
        {
           
            //IsHitTestVisible = false;
        }

        public override void OnRender(Graphics g)
        {
            //將距離轉換成畫素長度
            int R = (int)(RawRadius / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;
           
            if (IsFilled)
            {
                g.FillEllipse(Fill, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R));
            }
            g.DrawEllipse(Stroke, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R));
        }

        public override void Dispose()
        {
            if (Stroke != null)
            {
                Stroke.Dispose();
                Stroke = null;
            }

            if (Fill != null)
            {
                Fill.Dispose();
                Fill = null;
            }

            base.Dispose();
        }

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);

            // TODO: Radius, IsFilled
        }

        protected GMapMarkerCircle(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            // TODO: Radius, IsFilled
        }

        #endregion
    }
矩形:
[Serializable]
    public class GMapMarkerRect : GMapMarker, ISerializable
    {
        [NonSerialized]
        public Pen Stroke=new Pen(Brushes.Blue);

        [NonSerialized]
        public GMarkerGoogle InnerMarker;

        [NonSerialized]
        public int BorderWidth =5;

        [NonSerialized]
        public Size RectSize =new Size(1000, 1000);

        public GMapMarkerRect(PointLatLng p)
            : base(p)
        {
            //IsHitTestVisible = false;
            Stroke.Width= BorderWidth;

            // do not forget set Size of the marker
            // if so, you shall have no event on it ;}
        }

        public override void OnRender(Graphics g)
        {
            //將距離轉換成畫素長度
            int R = (int)(RectSize.Width / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat));

            //每次重新渲染的時候,重新計算Marker的大小和偏移。OnMarkerClick事件按Size和Offset的Marker來進行觸發
            Offset = new Point(-R / 2, -R / 2); 
            Size = new Size(R, R);

            g.DrawRectangle(Stroke, new Rectangle(LocalPosition.X , LocalPosition.Y , R, R));
        }

        public override void Dispose()
        {
            if (Stroke != null)
            {
                Stroke.Dispose();
                Stroke = null;
            }

            if (InnerMarker != null)
            {
                InnerMarker.Dispose();
                InnerMarker = null;
            }

            base.Dispose();
        }

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }

        protected GMapMarkerRect(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        #endregion
    }

扇形:
 [Serializable]
    public class GMapMarkerSector : GMapMarker, ISerializable
    {
        [NonSerialized]
        public Pen Stroke = new Pen(Brushes.Blue);

        [NonSerialized]
        public GMarkerGoogle InnerMarker;

        [NonSerialized]
        public int BorderWidth = 3;

        //[NonSerialized]
        //public int Angle = 90;

        [NonSerialized]
        public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.Aqua));

        [NonSerialized]
        public int RawRadius = 100;

        public GMapMarkerSector(PointLatLng p, int radius)
            : base(p)
        {
            //IsHitTestVisible = false;
            Radius = RawRadius = radius;
            //Size = new Size(RawRadius / 2, RawRadius / 2);
        }

        public override void OnRender(Graphics g)
        {
            int R = (int)(RawRadius*2 / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;
            Radius = R;
            
            g.FillPie(Fill, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R), (float)(-90 + (FwAngle - Angle)), Angle * 2);
            g.DrawPie(Stroke, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R), (float)(-90 + (FwAngle - Angle)), Angle * 2);
        }

        public override void Dispose()
        {
            if (Stroke != null)
            {
                Stroke.Dispose();
                Stroke = null;
            }
            if (Fill != null)
            {
                Fill.Dispose();
                Fill = null;
            }
            if (InnerMarker != null)
            {
                InnerMarker.Dispose();
                InnerMarker = null;
            }
            base.Dispose();
        }

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }

        protected GMapMarkerSector(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        #endregion

    }

箭頭:
 [Serializable]
    public class GMarkerArrow : GMapMarker, ISerializable
    {
        [NonSerialized]
        public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.Blue));

        //[NonSerialized]
        //public Pen Pen = new Pen(Brushes.Blue, 5);

        static readonly Point[] Arrow = { new Point(-7, 7), new Point(0, -22), new Point(7, 7), new Point(0, 2) };

        public float Bearing = 0;
        private float scale = 1;

        public float Scale
        {
            get
            {
                return scale;
            }
            set
            {
                scale = value;

                Size = new Size((int)(14 * scale), (int)(14 * scale));
                Offset = new Point(-Size.Width / 2, (int)(-Size.Height / 1.4));
            }
        }

        public GMarkerArrow(PointLatLng p)
            : base(p)
        {
            Scale = 1;
        }

        public override void OnRender(Graphics g)
        {
            //g.DrawRectangle(Pen, new System.Drawing.Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height));
            {
                g.TranslateTransform(ToolTipPosition.X, ToolTipPosition.Y);
                var c = g.BeginContainer();
                {
                    g.RotateTransform(Bearing - Overlay.Control.Bearing);
                    g.ScaleTransform(Scale, Scale);

                    g.FillPolygon(Fill, Arrow);
                }
                g.EndContainer(c);
                g.TranslateTransform(-ToolTipPosition.X, -ToolTipPosition.Y);
            }
        }

        public override void Dispose()
        {
            //if(Pen != null)
            //{
            //   Pen.Dispose();
            //   Pen = null;
            //}

            if (Fill != null)
            {
                Fill.Dispose();
                Fill = null;
            }

            base.Dispose();
        }

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }

        protected GMarkerArrow(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        #endregion
    }

呼叫
  //畫圓
        void AddCircle(PointLatLng point)
        {
            var cc = new GMapMarkerCircle(point, 1000)
            {
                //背景色填充
                Fill = new SolidBrush(Color.FromArgb(100, Color.CadetBlue)),
                //邊框顏色
                Stroke = new Pen(Color.FromArgb(100, Color.Aqua)),
                ToolTipText = "測試圓",
                ToolTipMode = MarkerTooltipMode.OnMouseOver,
                IsCircle = true
            };
            objects.Markers.Add(cc);
        }

        //畫矩形
        void AddRect(PointLatLng point)
        {
            GMapMarkerRect mBorders = new GMapMarkerRect(point)
            {
                ToolTipMode = MarkerTooltipMode.OnMouseOver,
                ToolTipText = "123",
                //滾輪放大放小地圖不受影響
                //IsHitTestVisible = false,
                BorderWidth = 1,
                Stroke = new Pen(Brushes.DarkCyan)
            };
            objects.Markers.Add(mBorders);
        }

        //畫箭頭
        void AddArrow(PointLatLng point)
        {
            GMapMarker marker = new GMarkerArrow(point)
            {
                Fill = new SolidBrush(Color.FromArgb(255, Color.Coral)),
                Scale = 2,
                Bearing = 2
            };
            objects.Markers.Add(marker);
        }

        //畫扇形
        void AddSector(PointLatLng point)
        {
            GMapMarkerSector st = new GMapMarkerSector(point,1000)
            {
                //填充顏色
                Fill = new SolidBrush(Color.FromArgb(100, Color.CadetBlue)),
                //邊框顏色、寬度
                Stroke = new Pen(Color.FromArgb(100, Color.DodgerBlue), 1),
                Angle = 60,
                FwAngle = 0,
                Radius = 1000,
                IsSector = true,
                ToolTipMode = MarkerTooltipMode.OnMouseOver,
                ToolTipText = "123",
            };
            _markersOverlay.Markers.Add(st);
        }

三、GMap機制和重寫

1.GMarker底層機制預設IsHitTestVisible=true,導致滑鼠在Marker上的時候拖動不了地圖圖層和滾輪縮放地圖。個人之前開發慣B端地圖專案,覺得這是一個很不好的體驗。改改改,GMapControl改底層事件 2.GMarker底層區域是一個矩形,無論你自定義什麼樣的圖形(圓、扇形)。它的底層捕捉都是基於GMarker的Size屬性所畫一個矩形區域,所有導致圓形、扇形這些圖形的地圖點選事件範圍擴大成一個矩形,這個不是我想要的效果,而且當marker集中在一個區域的時候這個問題將會放大!so,重寫點選事件機制 3.重寫ToolTipText提示文字顯示,GMapToolTip和GMapRoundedToolTip這兩個類

4.修改GMarker點選事件,原來只返回一個GMarker物件,重寫後返回匹配的所有GMarker列表。

 gmap.OnMarkerClick2 += gmap_OnMarkerClick2;
void gmap_OnMarkerClick2(List<GMapMarker> item, MouseEventArgs e){}

下載地址