深入理解最强桌面地图控件GMAP.NET --- 街景地图(StreetView)

很久没有更新博客了,今天无事把GMAP.NET的代码又重新翻了翻,看到了街景地图的例子。

街景地图是谷歌最早提出来的,我不知道谷歌的街景地图是如何实现的,在这个例子中,运用了WPF 3D的原理,对街景地图进行了简单的实现,在我看来更像是全景地图(PanoramaViewer)。先看看实现的效果,在本地运行代码的时候,鼠标拖动后整个图像是可以360旋转的,这里是张静态图片而已。

整篇文档需要对WPF 3D有个基本的了解,至少要知道Viewport3D(视野),PerspectiveCamera(摄像机),ModelVisual3D等概念,如果没有这些概念,可以先去msdn看一下相关的基础知识。因为整篇文档的技术部分其实和地图没有直接的关系,更多是讲3D。

整个项目的所有代码就是3个文件,App.xaml,PanoramaViewer.cs,Window1.xaml。

App.xaml是创建工程时默认生成的;

Window1.xaml主要完成了加载图片并放入到PanoramaViewer的工作;

PanormaViewer, Panorma的英文意思是全景,因此我们给它取了个名字叫全景查看器,这个类是整个项目的核心。

1. 核心类 PanoramaViewer(全景查看器)

整个PanormaViewer继承于Viewport3D,构造了一个最简单的3D模型,里面很多属性,例如FieldOfView, RotationX, RotationY, RotationZ,ModelVisual3D,GeometryModel3D等都是和WPF 3D息息相关的。只是PanoramaImage ImageSource的构造需要注意一下。

具体的代码如下所示:

View Code

 

2. 图片的组织和加载

Window1.xaml则承担了图片的组织和加载工作,和大部分图片加载一样,也是先尝试从本地加载,本地没有,则从网上下载。这里的图片是由许多

小块组成的,看看图片文件夹的结构就清楚了。最后这些图片组成RenderTargetBitmap,赋给前面提到的PanoramaImage。

下面是Window1.xaml的代码:

View Code
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Demo.StreetView
{
   /// <summary>
   /// Interaction logic for Window1.xaml
   /// </summary>
   public partial class Window1 : Window
   {
      BackgroundWorker loader = new BackgroundWorker();
      StackPanel buff = new StackPanel();

      public Window1()
      {
         InitializeComponent();
         Viewer.MouseLeftButtonDown += Viewer_MouseLeftButtonDown;
         Viewer.MouseMove += Viewer_MouseMove;

         buff.Orientation = Orientation.Vertical;

         // removes white lines between tiles!
         SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);

         loader.DoWork += loader_DoWork;
         loader.ProgressChanged += loader_ProgressChanged;
         loader.RunWorkerCompleted += loader_RunWorkerCompleted;
         loader.WorkerReportsProgress = true;
      }

      void loader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         buff.UpdateLayout();

         Canvas canvas = new Canvas();
         canvas.Children.Add(buff);
         canvas.Width = 512 * 13;
         canvas.Height = 512 * 7;

         canvas.UpdateLayout();

         canvas.Measure(new Size((int)canvas.Width, (int)canvas.Height));
         canvas.Arrange(new Rect(new Size((int)canvas.Width, (int)canvas.Height)));
         int Height = ((int)(canvas.ActualHeight));
         int Width = ((int)(canvas.ActualWidth));

         RenderTargetBitmap _RenderTargetBitmap = new RenderTargetBitmap(Width, Height, 96, 96, PixelFormats.Pbgra32);
         _RenderTargetBitmap.Render(buff);

         Image img = new Image();
         img.Source = _RenderTargetBitmap;

         Viewer.PanoramaImage = _RenderTargetBitmap;

         Title = "Demo.StreetView, enjoy! ;}";
      }

      Vector RotationVector = new Vector();
      Point DownPoint = new Point();
      void Viewer_MouseMove(object sender, MouseEventArgs e)
      {
         if(e.LeftButton == MouseButtonState.Released)
            return;
         Vector Offset = Point.Subtract(e.GetPosition(Viewer), DownPoint) * 0.25;

         Viewer.RotationY = RotationVector.Y + Offset.X;
         Viewer.RotationX = RotationVector.X - Offset.Y;
      }

      void Viewer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      {
         DownPoint = e.GetPosition(Viewer);
         RotationVector.X = Viewer.RotationX;
         RotationVector.Y = Viewer.RotationY;
         Cursor = Cursors.SizeAll;
      }

      private void Viewer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
      {
         Cursor = Cursors.Arrow;
      }

      void loader_ProgressChanged(object sender, ProgressChangedEventArgs e)
      {
         if(e.ProgressPercentage == 100)
         {
            Pass p = e.UserState as Pass;

            Title = "Demo.StreetView, please wait on first time loading: " + p.X + "|" + p.Y + " of 13";
            Image i = new Image();

            i.Source = p.src;
            (buff.Children[buff.Children.Count - 1] as StackPanel).Children.Add(i);
         }
         else if(e.ProgressPercentage == 0)
         {
            Title = "Demo.StreetView, please wait on first time loading: zooming...";

            StackPanel ph = new StackPanel();
            ph.Orientation = Orientation.Horizontal;
            buff.Children.Add(ph);
         }
      }

      void loader_DoWork(object sender, DoWorkEventArgs e)
      {
         string panoId = "4fe6hEN9GJC6thoQBcgv0Q";
         int zoom = 4;

         //0, 1
         //1, 2   
         //2, 4
         //3, 7   
         //4, 13  
         //5, 26  

         for(int y = 0; y <= zoom + 1; y++)
         {
            loader.ReportProgress(0);

            for(int x = 0; x < 13; x++)
            {
               Pass p = new Pass();
               p.Y = y;
               p.X = x;

               string fl = "Tiles\\" + zoom + "\\" + panoId + "\\img_" + x + "_" + y + ".jpg";
               string dr = System.IO.Path.GetDirectoryName(fl);
               if(!Directory.Exists(dr))
               {
                  Directory.CreateDirectory(dr);
               }
               if(!File.Exists(fl))
               {
                  ImageSource src = Get(string.Format("http://cbk{0}.{5}/cbk?output=tile&panoid={1}&zoom={2}&x={3}&y={4}&cb_client=maps_sv", (x + 2 * y) % 3, panoId, zoom, x, y, GMap.NET.MapProviders.GoogleMapProvider.Instance.Server));
                  p.src = src;
                  SaveImg(src, fl);
               }
               else
               {
                  using(Stream s = File.OpenRead(fl))
                  {
                     p.src = FromStream(s);
                  }
               }

               loader.ReportProgress(100, p);
            }
         }

         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
      }

      void SaveImg(ImageSource src, string file)
      {
         using(Stream s = File.OpenWrite(file))
         {
            JpegBitmapEncoder e = new JpegBitmapEncoder();
            e.Frames.Add(BitmapFrame.Create(src as BitmapSource));
            e.Save(s);
         }
      }

      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
         loader.RunWorkerAsync();
      }

      public Stream CopyStream(Stream inputStream)
      {
         const int readSize = 256;
         byte[] buffer = new byte[readSize];
         MemoryStream ms = new MemoryStream();

         using(inputStream)
         {
            int count = inputStream.Read(buffer, 0, readSize);
            while(count > 0)
            {
               ms.Write(buffer, 0, count);
               count = inputStream.Read(buffer, 0, readSize);
            }
         }
         buffer = null;
         ms.Seek(0, SeekOrigin.Begin);
         return ms;
      }

      ImageSource FromStream(Stream stream)
      {
         ImageSource ret = null;
         if(stream != null)
         {
            {
               // try png decoder
               try
               {
                  JpegBitmapDecoder bitmapDecoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                  ImageSource m = bitmapDecoder.Frames[0];

                  if(m != null)
                  {
                     ret = m;
                  }
               }
               catch
               {
                  ret = null;
               }

               // try jpeg decoder
               if(ret == null)
               {
                  try
                  {
                     stream.Seek(0, SeekOrigin.Begin);

                     PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                     ImageSource m = bitmapDecoder.Frames[0];

                     if(m != null)
                     {
                        ret = m;
                     }
                  }
                  catch
                  {
                     ret = null;
                  }
               }
            }
         }
         return ret;
      }

      ImageSource Get(string url)
      {
         ImageSource ret;
         
          try
         {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.ServicePoint.ConnectionLimit = 50;
            request.Proxy = WebRequest.DefaultWebProxy;

            request.UserAgent = "Opera/9.62 (Windows NT 5.1; U; en) Presto/2.1.1";
            request.Timeout = 10 * 1000;
            request.ReadWriteTimeout = request.Timeout * 6;
            request.Referer = string.Format("http://maps.{0}/", GMap.NET.MapProviders.GoogleMapProvider.Instance.Server);
            request.KeepAlive = true;

            using(HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
               using(Stream responseStream = CopyStream(response.GetResponseStream()))
               {
                  ret = FromStream(responseStream);
               }
            }
         }
         catch(Exception)
         {
            ret = null;
         }
         return ret;
      }
   }

   class Pass
   {
      public ImageSource src;
      public int Y;
      public int X;
   }
}

整个代码在http://code.google.com/p/ypmap/source/browse/可以看到。

posted on 2013-04-30 22:29  enjoyeclipse  阅读(6589)  评论(2编辑  收藏  举报