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

将 Open Street Map 图块地理参考化以与 MapWinGIS 一起使用

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2012年9月19日

CPOL

3分钟阅读

viewsIcon

57782

downloadIcon

3505

如何在 Map 中使用 MapWinGIS 添加 Open Street Map 叠加层。

介绍 

查看 GIS 数据(例如 Shape 文件)的问题之一是很难浏览它们,因为通常没有常见的地标。一个显而易见的解决方案是添加一个透明地图作为叠加层,并参考相同的坐标方案。 Open Street Map 数据可以免费获得,以多种分辨率的渲染切片形式提供,几乎覆盖了整个地球。 MapWinGIS 支持将图像文件叠加到地图上,只要它们已进行地理配准,因此相对容易添加动态地图叠加层,该叠加层可随 MapWinGIS 窗口缩放,以在所有分辨率下保持 OpenStreetMap 叠加层

背景 

有关 Open Street Map 切片(Slippy Maps)的详细信息,请参见此处:http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers。

有关用于地理配准图像文件的 World Files 的详细信息,请参见此处:http://en.wikipedia.org/wiki/World_file

定位 Slippy 切片

以下两个函数来自 Slippy Tile Wiki,可在 Slippy Map Tile 引用和 WGS-84 纬度和经度之间进行转换。这些函数用于计算覆盖地图区域所需的切片,然后在将每个切片添加到地图之前对其进行地理配准。

有关该方法以及 Slippy 切片如何编号的详细信息,请参见此处:http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Derivation_of_tile_names  

Private Function CalcTileXY(ByVal LatDeg As Single, _
                            ByVal LonDeg As Single, _
                            ByVal ZoomLevel As Long) As PointF
 
 CalcTileXY.X = CLng(Math.Floor((LonDeg + 180) / 360 * 2 ^ ZoomLevel))
 CalcTileXY.Y = CLng(Math.Floor((1 - Math.Log(Math.Tan(LatDeg * Math.PI / 180)+ 1
                                             / Math.Cos(LatDeg * Math.PI / 180)) 
                                             / Math.PI) / 2 * 2 ^ ZoomLevel))
End Function
 
   
Private Function CalcTileTLCoord(ByVal TileCoord As PointF, _
                                 ByVal ZoomLevel As Long) As PointF
    Dim n As Double = Math.PI - ((2.0 * Math.PI * TileCoord.Y) / Math.Pow(2.0, ZoomLevel))
    CalcTileTLCoord.X = (TileCoord.X / Math.Pow(2.0, ZoomLevel) * 360) - 180
    CalcTileTLCoord.Y = 180 / Math.PI * Math.Atan(Math.Sinh(n))
 
End Function

创建地图叠加层

为了创建地图叠加层,我们需要计算要覆盖的区域。可以通过查看 AxMapWinGIS 地图的范围来找到它: 

Dim MapExtents As MapWinGIS.Extents = MapMain.Extents
Dim LonMin As Double = MapExtents.xMin
Dim LonMax As Double = MapExtents.xMax
Dim LatMin As Double = MapExtents.yMin
Dim LatMax As Double = MapExtents.yMax

注意:此方法仅在 AxMapWinGIS 地图使用与 OSM Slippy 切片相同的坐标系(即 WGS-84)时才有效。否则,您必须将 WGS-84 转换为地图的坐标系,反之亦然。  

一旦我们获得了由最小和最大纬度和经度定义的区域,我们就可以使用其中一个 Slippy Map 函数将坐标转换为地图左上角和右下角的切片参考: 

TLTile = CalcTileXY(LatMax, LonMin, ZoomLevel)      'Get the Tile reference for the Top Left Tile
BRTile = CalcTileXY(LatMin, LonMax, ZoomLevel)      'Get the Tile reference for the Bottom Right Tile

然后,可以简单地遍历两个角之间的所有切片,以创建覆盖整个地图区域的地图切片镶嵌图: 

' Now add each tile in turn to the map
Dim H As Integer, V As Integer          ' Loop parameters
Dim TileRef As Point
 
For H = TLTile.X To BRTile.X            ' H is looping through Longitude
    For V = TLTile.Y To BRTile.Y        ' V is looping through Latitude
         TileRef.X = H
         TileRef.Y = V
         Call LoadOSMTile(MapMain, TileRef, ZoomLevel)
     Next V
Next H

下载和地理配准切片

每个切片都存储在 Slippy Tile 服务器上的目录结构中: 

Const ServerName As String = "http://a.tile.openstreetmap.org/"
Dim URL As String = ServerName & ZoomLevel & "/" & TileRef.X & "/" & TileRef.Y & ".png"

要下载切片,我们使用 Net.HttpWebRequest

'Get the image from the web server
Dim request As Net.HttpWebRequest = DirectCast(Net.HttpWebRequest.Create(URL), Net.HttpWebRequest)
Dim response As Net.HttpWebResponse = DirectCast(request.GetResponse, Net.HttpWebResponse)
Dim Image As System.Drawing.Image = Image.FromStream(response.GetResponseStream())
response.Close()

下载切片后,我们将其保存在本地缓存中

'Save the image in the local cache
Image.Save(CacheTileDirectory & "\" & CacheFileName)

并通过为切片创建一个 World File(对与 .png 文件关联的 World 文件使用 .pgw 扩展名)对其进行地理配准

' We georeference the image using the Top Left Coordinate 
Dim TileTLCoord As PointF = CalcTileTLCoord(TileRef, ZoomLevel)

' We also need the width and height of the image in the map coordinates (degrees)
' We get this by subtracting the difference between this tile and the next tile
Dim Point2 As PointF
Point2.X = TileRef.X + 1
Point2.Y = TileRef.Y + 1
Dim TileWidthDeg As Double = CalcTileTLCoord(Point2, ZoomLevel).X - CalcTileTLCoord(TileRef, ZoomLevel).X
Dim TileHeightDeg As Double = CalcTileTLCoord(TileRef, ZoomLevel).Y - CalcTileTLCoord(Point2, ZoomLevel).Y
Debug.Print("Tile Width = " & TileWidthDeg & ", Height = " & TileHeightDeg)
 
'Create World file to geo-reference the PNG. See: http://en.wikipedia.org/wiki/World_file
Dim oWrite As System.IO.StreamWriter = IO.File.CreateText(CacheTileDirectory & _
    "\" & Left(CacheFileName, CacheFileName.Length - 4) & ".pgw")
oWrite.WriteLine(FormatNumber(TileWidthDeg / 256, 10))           'width of each pixel in map units (degrees)
oWrite.WriteLine("0.0")                                          'rotation parameter (not used in this case)
oWrite.WriteLine("0.0")                                          'rotation parameter (not used in this case)
oWrite.WriteLine(FormatNumber(-1 * TileHeightDeg / 256, 10))     'height of each pixel in map units (degrees)
oWrite.WriteLine(FormatNumber(TileTLCoord.X, 10))                'x coordinate of the Upper LH cell (degrees)
oWrite.WriteLine(FormatNumber(TileTLCoord.Y, 10))                'y coordinate of the Upper LH cell (degrees)
oWrite.Close()

请注意,高度为负,表示像素从左上角(地理参考点)垂直向下。

将 World File 保存到与 PNG 图像相同的目录后,我们可以将 PNG 文件添加到地图。 MapWinGIS 将自动检测 World File,只要它位于同一目录中

'Load the geo-referenced PNG Slippy Tile (MapWinGIS auto detects 
the World file and loads that at the smae time)
Dim ImageType As MapWinGIS.ImageType = MapWinGIS.ImageType.PNG_FILE
Dim MWImage As New MapWinGIS.Image
If Not MWImage.Open(CacheTileDirectory & "\" & CacheFileName, ImageType) Then
     Debug.Print("Image load failed: " & MWImage.ErrorMsg(MWImage.LastErrorCode))
     Exit Sub
End If
 
'Set Transparency to 50%
MWImage.TransparencyPercent = 0.5
 
'Add to the Map as a new layer
Dim PNGLayer As Integer = MapMain.AddLayer(MWImage, True)

使用代码

完整的 Visual Studio 2010 项目包括上述代码和一个 Windows Form 包装器,该包装器允许用户导航 Shape 文件,放大和缩小。鼠标位置显示在屏幕底部的文本窗口中,因此您可以通过定位已知特征等来检查叠加层是否正确

 

唯一需要的外部参考是要加载的 Shape 文件,并且该代码当前使用 MapWindow 示例项目文件之一,该文件随 MapWindow 一起安装。

"C:\Program Files\MapWindow\Sample Projects\World\Shapefiles\WORLD30.shp"

因此,请确保它存在,或选择另一个文件进行操作。

历史

第一个版本 2012 年 9 月 19 日。

© . All rights reserved.