深入理解最強桌面地圖控件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/可以看到。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM