深入理解最强桌面地图控件GMAP.NET --- 街景地图(StreetView)
原文:深入理解最强桌面地图控件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的构造需要注意一下。
具体的代码如下所示:
2. 图片的组织和加载
Window1.xaml则承担了图片的组织和加载工作,和大部分图片加载一样,也是先尝试从本地加载,本地没有,则从网上下载。这里的图片是由许多
小块组成的,看看图片文件夹的结构就清楚了。最后这些图片组成RenderTargetBitmap,赋给前面提到的PanoramaImage。

下面是Window1.xaml的代码:
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 = * ;
canvas.Height = * ; 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, , , 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 == )
{
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 - ] as StackPanel).Children.Add(i);
}
else if(e.ProgressPercentage == )
{
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 = ; //0, 1
//1, 2
//2, 4
//3, 7
//4, 13
//5, 26 for(int y = ; y <= zoom + ; y++)
{
loader.ReportProgress(); for(int x = ; x < ; 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 + * y) % , 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(, 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 = ;
byte[] buffer = new byte[readSize];
MemoryStream ms = new MemoryStream(); using(inputStream)
{
int count = inputStream.Read(buffer, , readSize);
while(count > )
{
ms.Write(buffer, , count);
count = inputStream.Read(buffer, , readSize);
}
}
buffer = null;
ms.Seek(, 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[]; if(m != null)
{
ret = m;
}
}
catch
{
ret = null;
} // try jpeg decoder
if(ret == null)
{
try
{
stream.Seek(, SeekOrigin.Begin); PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
ImageSource m = bitmapDecoder.Frames[]; 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 = ;
request.Proxy = WebRequest.DefaultWebProxy; request.UserAgent = "Opera/9.62 (Windows NT 5.1; U; en) Presto/2.1.1";
request.Timeout = * ;
request.ReadWriteTimeout = request.Timeout * ;
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/可以看到。
深入理解最强桌面地图控件GMAP.NET --- 街景地图(StreetView)的更多相关文章
- 深入理解最强桌面地图控件GMAP.NET ---离线地图
enjoyeclipse 深入理解最强桌面地图控件GMAP.NET ---离线地图 这章会介绍GMAP.NET的核心功能之一:离线地图.这个功能可以满足很多政府项目.保密项目.或者由于种种原因不能上网 ...
- (转)深入理解最强桌面地图控件GMAP.NET --- 百度地图
原文地址:http://www.cnblogs.com/enjoyeclipse/archive/2013/01/14/2859026.html 前两篇介绍了GMAP.NET的一些基本功能和如何在自己 ...
- 深入理解最强桌面地图控件GMAP.NET ---[更新]百度地图
之前写了篇博文,深入理解最强桌面地图控件GMAP.NET --- 百度地图 但是很多回复说百度地图更新了,不能显示百度的离线地图.之前承诺说是国庆节更新,最近才更新.代码已经提交到: https:// ...
- 如何添加地图控件到Windows Phone 8的页面中
原文 如何添加地图控件到Windows Phone 8的页面中 本主题介绍了各种方法来添加一个地图控件到Windows Phone 8的项目.该地图控件在Windows Phone的SDK 8.0的库 ...
- OpenLayers 3 之 地图控件(control)
OpenLayers 3 之 地图控件(control) 地图控件(control)是指地图上比例尺,缩略图,拉近拉远的按钮,滚动控制条等控件,默认控件有三个,可以禁用. OpenLayers 3 之 ...
- Google地图接口API之地图控件集(五)
1.默认控件集 当使用一个标准的google地图,它的控件默认设置如下: (1). Zoom-显示一个滑动条来控制map的Zoom级别,如下所示:
- Corelocation及地图控件学习笔记
Corelocation基本使用 在地图章节的学习中,首先要学的便是用户位置定位,因此我们首先要掌握Corelocation的使用.(在IOS8以前可以系统会直接请求授权,现在需要我们自己调用方式通知 ...
- 使用XAML在WPF项目中承载ArcGIS Engine地图控件开发
原文 http://blog.csdn.net/flexmapserver/article/details/5868882 用Windows Form进行ArcGIS Engine二次开发时常见的形式 ...
- 【高德地图API】从零开始学高德JS API(二)地图控件与插件——测距、圆形编辑器、鼠标工具、地图类型切换、鹰眼鱼骨
原文:[高德地图API]从零开始学高德JS API(二)地图控件与插件——测距.圆形编辑器.鼠标工具.地图类型切换.鹰眼鱼骨 摘要:无论是控件还是插件,都是在一级API接口的基础上,进行二次开发,封装 ...
随机推荐
- VC/MFC中为程序定义全局快捷键
VC 2010-05-01 18:01:34 阅读287 评论0 字号:大中小 订阅 1.注册快捷键 在初始化函数,如OnInitDialog() 注册快捷键,代码如下: #define HotKey ...
- keil快捷键
- 使用JQuery将前端form表单数据转换为JSON字符串传递到后台处理
一般地,我们在处理表单(form表单哦)数据时,传输对象或字符串到后台,Spring MVC或SpringBoot的Controller接收时使用一个对象作为参数就可以被正常接收并封装到对象中.这种方 ...
- vs 外部依赖项、附加依赖项以及如何添加依赖项目
我们在 VS 中创建 Win32 控制台应用程序,vs 会为解决方案创建默认地创建 4 个 filters(资源管理器中没有对应的目录和文件夹): 头文件:一般为 .h 文件 外部依赖项 源文件:一般 ...
- 驱动程序调试方法之printk——printk的原理与直接使用
1.基本原理 (1)在UBOOT里设置console=ttySAC0或者console=tty1 这里是设置控制终端,tySAC0 表示串口, tty1 表示lcd(2)内核用printk打印 内核就 ...
- Qt on Android: http下载与Json解析
百度提供有查询 ip 归属地的开放接口,当你在搜索框中输入一个 ip 地址进行搜索,就会打开由 ip138 提供的百度框应用,你能够在框内直接输入 ip 地址查询.我查看了页面请求,提取出查询 ip ...
- javax.servlet.WriteListener
http://www.programcreek.com/java-api-examples/index.php?api=javax.servlet.WriteListener
- Qt 无标题无边框程序的拖动和改变大小
最近做项目遇到的问题,总结下. 有时候我们觉得系统的标题栏和按钮太丑太呆板,想做自己的标题栏以及最大化.最小化.关闭,菜单按钮,我们就需要 setWindowFlags(Qt::FramelessWi ...
- 前端切图:调用百度地图API
原型图 图片发自简书App <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- iOS 9和xcode7设置
升级了Xcode7各种问题来了,折腾两天 一.Xcode7 http适配设置 1.大部分社交平台接口不支持https协议. 2.大部分社交平台SDK不支持bitcode. 3.添加Scheme白名单 ...
