65.9K
CodeProject 正在变化。 阅读更多。
Home

Google Maps 离线 Windows

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (20投票s)

2010 年 6 月 8 日

CPOL

11分钟阅读

viewsIcon

103455

downloadIcon

9392

Google Maps 离线 Windows 讨论了如何下载或保存 Google 地图, 然后离线查看这些图像

引言

在本文中,我将讨论一个问题,即如何在不使用 Microsoft 或 Google 地图服务等任何在线服务的情况下,计算特定位置的经纬度信息。尽管我从 Google 获取了图像数据,但所有计算经纬度的后台处理都是我使用墨卡托投影方法完成的。我包含了代码文件,以演示如何将地图上的鼠标位置转换为现实世界的经纬度值,这将在下图中进行说明。

Google Map Image

在上图的左上角,X、Y 显示了黄色指针的屏幕坐标,而经度和纬度显示了屏幕上黄色指针的现实世界经纬度。如果您运行本文附带的应用程序文件,您会发现在应用程序窗口中移动鼠标时,屏幕上会显示鼠标坐标(X、Y)及其对应的现实世界经纬度值。通过这种方式,您可以在 Windows 中离线使用 Google 地图。

问题是什么?

XNA 框架、Winforms API 和墨卡托投影公式等东西很容易获得。主要问题是将经纬度值转换为屏幕坐标。这是我一个项目中的需求。

您可以轻松理解墨卡托投影,但将其应用于 Google 图像并在计算机上进行编程则是一个稍具挑战性的问题。为了应用墨卡托投影,您需要计算半径、周长等不同参数。我已经解决了所有这些难题,并使用 C#.NET 实现墨卡托投影。与墨卡托投影类似,还有更高级的投影可用,它们更准确,但为了简单起见,我选择了墨卡托投影。您可以利用本文中的信息来应用不同的投影方法。

什么是墨卡托投影?

简单来说,地图学领域的投影是将整个 3D 世界的地图绘制到 2D 表面上的方法。墨卡托投影就是一种投影。在墨卡托投影中,地球上的每一个点首先被投射到一个圆柱体的内表面,然后将这个圆柱体展开以显示整个地球。您可以在网上轻松找到墨卡托投影模型,维基百科上有关于墨卡托投影的详细描述。

墨卡托投影存在一些局限性。一个限制是公式未定义 90 度值,另一个是这些公式得到的值包含一些非常微小的误差。

Google 如何排列它们的地图?

Google 人员也使用墨卡托投影。他们以不同的缩放级别组织图像。缩放级别范围从 1 到 19。在每个缩放级别,他们将地图划分为瓦片。每个瓦片由 256*256 像素组成。每个缩放级别有不同数量的瓦片。每个缩放级别的瓦片数量由下式给出

Total number of tiles = 2 ^ (2*zoom Level)

因此,这表明缩放级别越高,瓦片数量越多,细节也越多。由于 Google 以瓦片形式组织了他们的地图,因此处理起来更方便,因为地图的最小单位不是米或厘米,而是像素,这在处理图形和计算机编程时更容易。

如何在 Windows 中离线查看 Google 地图

步骤 1

第一步是定义缩放级别。

this.zoomLevel = zoomLevel; // ranges from 1 to 19

第二步

在此步骤中,我们需要计算投影地图的周长、半径和中心。首先,我们计算周长。为了计算,我们应该知道缩放级别,然后计算投影的水平瓦片数量。

Math.Pow(2, zoomLevel); // this statement gives us the number of tiles 

之后,我们将其乘以 256,这是投影的水平长度,因此

this.circumference = 256 * Math.Pow(2, zoomLevel);

一旦我们知道周长,就可以轻松找到投影的半径和中心。

Circumference = 2 * Pi * radius

因此:

this.radius = (this.circumference) / (2 * Math.PI); 

同样,为了计算中心

this.centre = new RTPoint(this.circumference / 2, this.circumference / 2);

其中 RTPoint 是一个类,用于定义 x, y 平面中的任意二维点。

步骤 3

在第 3 步中,我们将从经度计算 x,从纬度计算 y。

从经度计算 x 要容易得多。您只需将经度值乘以半径即可。

public double getXFromLongitude(double longInDegrees)
{ 
double x = 0;
double longInRadians = longInDegrees * Math.PI / 180;
x = this.radius *
longInRadians; 

之后,您需要计算假东移。假东移是为 X 计算的,因为它表示原点的水平偏移。在计算假东移时,有时需要加上中心的 x 分量,有时需要从中减去。这将取决于您考虑的图像部分或 x 值。例如,如果您要获取印度或巴基斯坦的图像,您需要将其加到中心;如果您考虑美国的图像,您需要从中心减去。

x = this.centre.getPointX()+ x; // adding the x to centre’s x 
return x; 

现在,对于从纬度计算 y,您需要应用墨卡托投影的公式,其在编程语言中的实现如下:

public double getYFromLatitude(double latInDegrees) 
double y;
double latInRadians = latInDegrees * Math.PI / 180;
// Log for the base of E i.enatural logarthim..
double logVal = Math.Log(((1 + Math.Sin(latInRadians)) / (1 - Math.Sin(latInRadians))),
 Math.E);
y = this.radius * 0.5 * logVal;

上面语句中需要注意的是,所用对数的基是自然对数 (E)。

同样,在从纬度计算 y 时,我们需要计算假北移。同样,假北移特定于图像区域或 y 值。

// False Northing....
y = this.centre.getPointY() - y;
return y;  

到此为止,我们现在有了 x 和 y 值,但这些 x、y 值是针对完整的墨卡托投影的。这些值是投影的 x、y 值。要将它们转换为屏幕上的特定地图,我们需要进行一些转换,这些转换将在下一步中描述。

步骤 4

在此步骤中,我们需要将上面计算的 x、y 值转换为屏幕坐标。在较高的缩放级别,我们只能显示特定区域的地图。地图尺寸的限制受屏幕分辨率的约束。因此,为了首先处理图像的一部分,我们必须知道该图像部分的中心。在从 Google 地图服务获取图像时,您可以指定地图的中心和地图的分辨率,因此该中心是地图该部分的中心。在上图中,地图中心位于 latitude = 24.846605longitude = 67.028379,图像分辨率为 800 x 800 像素。

因此,我们找到此中心的 x、y 值。

double x = projection.getXFromLongitude(67.028379);
double y = projection.getYFromLatitude(24.846605);
screenMapCentre = new RTPoint (x, y);

之后,我将中心移动到屏幕的左上角,因为在计算机屏幕上,x、y 设置为 (0,0) 在左上角。如果您向右移动,x 值会增加;如果您向下移动,y 值会增加。因此,我们需要将中心移动到左上角。请注意,此移动与我们在假东移和假北移时进行的移动不同,此移动仅用于在计算机屏幕上工作。由于完整地图图像的分辨率为 800 x 800,因此为了移动中心,您需要从 x、y 中减去 400。

x = x - 400; 
y = y - 400;
shiftedCentre = new RTPoint(x, y); 

因此,要获得屏幕坐标,您需要从偏移中心减去投影坐标值。

aDot.X = (float)(projection.getXFromLongitude(longitudeInRadian)
 - shiftedCentre.getPointX());
aDot.Y = (float)(projection.getYFromLatitude(latitudeInRadian)
- shiftedCentre.getPointY()); 

通过这种方式,您可以获得任何位置的经纬度值到 x、y 格式的值,并且您可以在计算机屏幕的该 x、y 值上显示标记、点、箭头或其他任何内容。请注意,所有这些屏幕值仅针对您的应用程序,而不是针对整个屏幕。

从二维坐标转换为经纬度的步骤

在本文中,我提供的代码示例包含了将鼠标位置转换为经纬度的功能。在我的应用程序中,当您指向地图上的任何位置时,它都会在屏幕上显示经纬度。为了测试,您可以将此经纬度输入到 Google Earth 中,它将显示与我的应用程序显示的位置相同的位置。

将 2D 坐标转换为经纬度的方法就是上面方法的逆过程。步骤 1 和步骤 2 相似,因为它们是初始化步骤。

步骤 3

首先,我们必须从屏幕 x、y 坐标转换为墨卡托投影的 x、y 值,这与上面第 5 步的逆过程相同。

X = xMousePos +
shiftedCentre.getPointX()
Y = yMousePos + shiftedCentre.getPointY() 

步骤 4

在此步骤中,我将把 X 转换为经度,Y 转换为纬度。为了将 X 转换为经度,我们将执行以下代码行:

double longitude = 0; 
 // False Easting          
            xValue = xValue - this.centre.getPointX(); 
            longitude = xValue / this.radius; 
      longitude = longitude * 180 / Math.PI; 

请注意,这次我们也执行了假东移,这与我们从经度转换为 X 时所做的完全相反。

然后我们用以下代码将 Y 值转换为纬度:

double latitude =0;
// opposite of false northing.
yValue =  this.centre.getPointY()- yValue; 
double InvLog = yValue / (this.radius * 0.5);
InvLog = Math.Pow(Math.E, InvLog);
latitude =  Math.Asin(  (InvLog - 1) / (InvLog + 1) );
latitude = latitude * 180 / Math.PI;
return latitude;  

整个方法只是我们已经完成的上一个工作的逆过程。

因此,通过这种方式,我们已将 X、Y 值转换为经度和纬度值。

Microsoft XNA/WinformsFramework 如何帮助我?

以前,我曾用 XNA Framework 编写过这段代码,但 XNA Framework 已过时,我的读者抱怨他们无法在最新的 Visual Studio 版本中编译代码。因此,为了我的读者,我更新了代码,并用 WinForms 重写了示例源代码。现在您只需下载它并在您的项目中即可使用。

Microsoft XNA 是一个用于为爱好者和非专业游戏开发者创建游戏的绝佳框架。与 DirectX API 相比,它更容易。我选择了更简单的游戏 API,并在用户将鼠标光标移到计算机屏幕上时立即显示经纬度、X 和 Y 值。

如何在 Windows 中保存 Google 地图离线

您可以注册 Google Maps API 服务,并利用 Google 提供的密钥与它们一起工作,但我使用了 Google 代码 Playground 工具。Code Playground 是 Google Maps API 网站提供的一个工具,您可以在其中运行、测试和调试您的代码。该网站上有非常好的 Google Maps API 文档,您可以在其中阅读有关不同服务方法的信息。为了获取图像,我使用了以下服务方法:

     var map;
        functioninitialize() {
      if(GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map_canvas"));
      map.setMapType(G_SATELLITE_MAP); 
         map.setCenter(new GLatLng(24.846605, 67.028379),17); // defining the centre on 
					// the screen and zooming level.  

上面的 JavaScript 代码利用 Google 地图服务类 GMAP2 来创建地图,然后调用相关方法。

完整的地图将在 HTML 页面的 div 区域中显示。在 HTML 页面中,您可以通过定义 div 元素的宽度和高度来定义地图的像素数量,如下面的代码所示:

  <body onload="initialize()" onunload="GUnload()"> 
    <div id="map_canvas" style="width: 800px; height: 800px"></div>  

图像加载后,您可以截屏,然后借助 Microsoft Paint、Paint.NET 或 Photoshop 等图像编辑器,您可以提取图像,然后将其用于您的应用程序。这就是我为软件“Google Maps Offline Windows”获取图像的方式。

其他人还能做什么有趣的事情?

嗯,有很多事情可以玩,比如从 GPS 卡或其他 GPS 设备访问 GPS 坐标并在 GOOGLE 地图上显示。我是在 2D 中开发的,但您也可以将其转换为 3D。其他人也可以计算一种适当的方法来计算假东移和假北移。一种方法是根据 x 和 y 值来定义加或减。

借助这个简单的编程模型,您可以开发实时 GPS 系统,该系统可以将特定对象的 P31置显示在 Google 地图上。您还可以将此编程模型与加速度计一起使用,以显示实时位置,误差很小,因为商用 GPS 速度较慢,在高速行驶的车辆上放置时精度不高,因此您可以使用 3 轴加速度计构建自己的系统。

结论

许多同事问我为什么没有使用 Google Maps API。首先,Google Maps API 是在线的,无法离线访问。在我的应用程序中,我完全实现了墨卡托投影,因此您可以为任何应用程序修改和自定义它,尤其是在需要非常快速响应的情况下。您可以将其实现到任何应用程序中,从手机、GPS 导航到惯性导航系统,用于实时位置显示。嗯,有很多事情可以玩,比如从 GPS 卡或其他 GPS 设备访问 GPS 坐标并在 Google 地图上显示。

历史

  • 2010 年 6 月 8 日:初始帖子
  • 2018 年:将代码从 XNA 框架更新到 WinForms
© . All rights reserved.