SQL Server 和 MapPoint

使 MapPoint 2010 和 SQL Server Spatial 协同工作

Eric Frost

下载代码示例

在 Bing Maps 之后,Microsoft 提供了一些非常突出的地理空间技术,其中两种是 Microsoft MapPoint 2010 和 SQL Server 2008 R2 中的空间功能。然而,尽管 SQL Server 是存储地理空间数据的理想之选,而 MapPoint 也是一种优秀的地理空间查看器,但它们之间的通信并不如预想中的那么容易。

本文将演示如何从 SQL Server 读取点和多边形对象并在 MapPoint 中进行呈现。还将演示如何使用 Visual Studio 2010 中包含的 Entity Framework 4.0 将点和多边形写回 SQL Server。

为便于说明,文中将使用一家芝加哥公司“Al’s Beef”的餐厅位置和假设的交易区。在零售分析和建模过程中,可以使用不同参数定义交易区并可将其用于不同目标。通常将其定义为围绕某个店铺的最小区域,其中包含了满足特定阈值的地区,例如,50% 或 75% 的客户居住或工作的地区。本文中使用的所有交易区均使用 MapPoint 的“创建驾车时间区”功能创建,因此代表的是根据驾车时间所划定的假设交易区。

作为只有几十家店铺的连锁店,Al’s Beef 是一家规模相对较小的企业,但是,在此所使用的概念和技术,同样也适用于具有上千家连锁店的大型零售商以及其他行业和应用。

示例代码和“Al’s Beef”数据集(SQL 脚本格式)均可从 code.msdn.microsoft.com/mag201009Spatial 下载。

虽然本文并非过于技术化的文章,没有涉及最新语言或技术的复杂层面,但仍可用作常用 Microsoft 技术的有用组合的实用操作方法指导。文中说明了几个难点,包括实体框架不能直接理解 geography 对象以及 SQL Server Spatial 要求多边形必须为逆时针方向而 MapPoint 并无此要求。希望本文对于资深但由于缺少相关经验而不愿意进入地理空间领域的开发人员能够有所帮助,同时能向 MapPoint 开发人员演示如何成功利用 SQL Server 2008 R2。

设置数据库

为了按照本文中的代码示例操作,请先下载 SQL 脚本并在 SQL Server 中运行以设置数据库和对象。数据存储在 SQL Server 数据库中,名为“Corporate”,包括一张表、一个视图和一个存储过程。Al’s Beef 的位置存储在名为“Locations”的表中(参见图 1)。

图 1 示例数据库中包括的表和视图

这包括店铺的地址、属性(例如,是否免下车?)以及位置点 geography 数据类型。假设交易区多边形以 geography 数据类型也存储在“Locations”表的名为 TradeArea 的字段中。

vLocations 视图将点和多边形 geography 字段提供给可由实体框架理解和读取的数据类型。

点 geography 字段可分解为纬度和经度字段,并作为 varbinary 字段传递回客户端。这是由于实体框架无法直接处理 geography 数据类型,但可以处理 varbinary 字段。应用程序随后可以将这些内容转换回 geography 对象。

以下为存储过程 uspAddLocation,故名思义,它用于从 MapPoint 将新位置插入回 SQL Server 中:

CREATE VIEW [dbo].[vLocations]
AS
SELECT LocID,Location.Long As Longitude,
       Location.Lat As Latitude,
       CAST(Location AS VARBINARY(MAX)) AS Location,
       Locations.TradeArea.STAsText() As TradeAreaWKT,
       CAST(TradeArea AS VARBINARY(MAX)) AS TradeArea
FROM Locations

我们稍后再讨论此内容。

设置应用程序

此项目是集成了 MapPoint 控件的 C# Windows 窗体应用程序。该控件包括在 MapPoint 2010 中,要使用该控件,必须安装 MapPoint 2010 完整版。使用按钮遍历记录可导航到记录,并可显示店铺及其交易区。也可通过单击店铺的图钉图标来选择店铺。窗体还提供了用于将交易区显示为凸球面的复选框和用于添加新位置的按钮。默认情况下,应用程序按照多边形在数据库中的存储样式显示它(参见图 2)。

图 2 Al’s Beef 应用程序,按数据库中的定义显示芝加哥高地店铺和地域

如果选中了“以凸球面样式查看交易区”复选框,则将会使用线条(凸球面)环绕交易区,类似于使用橡胶带围起多边形(参见图 3)。

图 3 芝加哥高地店铺,使用凸球面围起了图 2 中所示地域

在显示地图之前,需要添加指向数据库表和视图的实体数据对象。要建立实体数据对象,请在 Visual Studio 解决方案资源管理器中右键单击应用程序,然后转到“添加 | 新建项 | Visual C# 项 | ADO.NET 实体数据模型”。单击“添加”并选择“从数据库生成”。在“选择数据库对象”对话框中,选择表 Locations 和视图 vLocations。单击“完成”后,该向导将创建对象并生成连接到数据库所需的代码。

要将 MapPoint 2010 控件添加到 Windows 窗体,则必须先将 MapPoint COM 控件组件添加到 Visual Studio 工具箱。COM 不是最流行的技术,但一直是 Windows 生态系统中的一个重要部分。包括 MapPoint 在内的许多应用程序仅通过 COM 接口实现 API,并且 Visual Studio 仍将持续提供对 COM 的支持。

打开 Visual Studio 工具箱,然后在“常规”部分中,右键单击并选择“选择项”。转到“COM 组件”选项卡,然后选择“Microsoft MapPoint Control 17.0”。MapPoint Control 17.0 即为 MapPoint 2010(北美或欧洲地区)。也可使用较早版本的 MapPoint(2002 版之前),但需要对名称稍作改动(例如,工具栏名称和符号标识符)。

在 AxInterop.MapPoint 和 Interop.MapPoint 程序集上,将“嵌入式互操作类型”属性设置为 False,并将“复制本地”设置为 True。现在可以将 MapPoint 控件拖动到窗体上并在应用程序中使用。

初始化 Map 窗体:加载 MapPoint

Map 窗体声明了多个成员变量,包括与实体框架的数据库连接、店铺信息列表以及将从视图中读取的店铺地理信息并行列表。变量 curLoc 跟踪应用程序中的当前店铺 ID,objMap 用于引用 MapPoint 控件的 map 对象,如此处所示:

namespace AlsBeef
{
 public partial class Map : Form
 {
  CorporateEntities db;
  List<Location> locationList;
  List<vLocation> vlocationList;
  int curLoc = -1;    // <0 indicates 'not set'
  MapPoint.Map objMap;
  MapPoint.Symbol objSymb;
  ...

创建窗体之后,将调用方法 CreateNewMapObject 以初始化地图控件并使用默认北美地图模板打开新地图。 此时已经设置了工具栏、定义了 objMap 并且已关闭兴趣点,以避免造成地图混乱(参见图 4)。 举例而言,“兴趣点”是 MapPoint 预定义的场所、餐厅和剧院。

图 4 创建窗体

public Map()
{
 InitializeComponent();
 CreateNewMapObject();
}


private void CreateNewMapObject()
{
  MPctrl.NewMap(GeoMapRegion.geoMapNorthAmerica);
  object barObj = "advanced";
  MPctrl.Toolbars.get_Item(refbarObj).Visible = true;
  MPctrl.Toolbars.LargeToolbarButtons = false;
  objMap = MPctrl.ActiveMap;
  // Make sure all points of interest are turned off
  objMap.PlaceCategories.Visible = MapPoint.GeoTriState.geoFalse;
}

窗体的 Load 方法填充店铺信息的两个列表。 locationList 包含所有常规的非地理信息,vlocationList 则读取由数据库视图转换的地理字段:

private void Map_Load(object sender, EventArgs e)
{
 db = new CorporateEntities();
 locationList = new List<Location>();
 vlocationList = new List<vLocation>();

 ObjectQuery<Location> locationQuery =
  db.Locations;
 ObjectQuery<vLocation> vlocationQuery =
  db.vLocations;

 locationList = locationQuery.ToList();
 vlocationList = vlocationQuery.ToList();

 InitMapSymb();
 InitMapPins();
 SetLocation(0);
}

最后两行则通过初始化地图来实际启动应用程序。 它们会为每个店铺位置添加图钉 (InitMapPins),并且定位地图和窗体控件以指向第一个店铺位置的数据 (SetLocation)。

向地图添加图钉

InitMapPins 方法中的情况更有趣:

private void InitMapPins()
  {
    MapPoint.Pushpin objPin = null;
    for (int i = 0; i < locationList.Count;
     i++)
    {
     MapPoint.Location objLoc =
      objMap.GetLocation(vlocationList[i].
Latitude.Value,
      vlocationList[i].Longitude.Value);
     objPin = objMap.AddPushpin(objLoc,   
      locationList[i].Name);
     objPin.Symbol = 145; // Red fork and knife
                         // (food, restaurant)
   }
  }

循环遍历 locationList 以检索由视图计算和公开的纬度和经度值。 这用于创建 MapPoint Location 对象,然后使用这些对象创建地图图钉(MapPoint Pushpin 对象)。 店铺名称用于 PushpinName 属性,该属性随后用于搜索和定位地图。 使用 MapPoint 内置的红色餐厅图钉符号(符号 #145)来表示图钉。 有关 MapPoint 2010 内置符号的完整列表,请参见 mapping-tools.com/info/pushpins/pushpins_2010.shtml。 本页还链接到了 MapPoint 较早版本中的图钉列表。

定位到当前记录并将多边形添加到地图中

使用 IncDecLocation 和 SetLocation 方法选择和显示新记录。 IncDecLocation 只是在当前位置 (curLoc) 应用增量 (cnt) 并将此新记录位置传递到 SetLocation:

private void IncDecLocation(int cnt = 0)
{
 // Apply the increment/decrement, wrapping around if necessary
 int newLoc = (curLoc + cnt + locationList.Count) % locationList.Count;

 SetLocation(newLoc);
}

SetLocation 例程是应用程序的作业主力。 SetLocation 选择新记录位置并在地图上显示它。 SetLocation 还从以前的图钉(如果有)上删除突出显示标记,并从地图上清除所有以前的交易区多边形(参见图 5)。

图 5 SetLocation 例程是应用程序的作业主力

private void SetLocation(int newLoc)
{
  MapPoint.Pushpin objPin = null;

  // Unhighlight previous pushpin
  If (curLoc>= 0)
  {
    objPin = (MapPoint.Pushpin)
     objMap.FindPushpin(locationList[curLoc].Name);
    objPin.Highlight = false;
  }

  // Clear all previous shapes
  while(objMap.Shapes.Count> 0)
  {
    objMap.Shapes[1].Delete();
  }
 

  // Set the new location
  curLoc = Math.Min( Math.Max(newLoc,0), locationList.Count-1);

  objPin = (MapPoint.Pushpin)
   objMap.FindPushpin(locationList[curLoc].Name);
  objMap.Location = objPin.Location;

...

接下来的部分会有一些棘手。 首先,应用程序检查“以凸球面样式查看交易区”复选框的状态。 如果未选中该复选框,则将采用定义多边形的 Well-Known Text (WKT) 字符串并将其传递给自定义 RenderPolygon 方法,以将其解析和呈现为地图上的多边形。

如果选中了该复选框,则提取地域多边形的 varbinary 对象并使用 System.IO.MemoryStream 类和 BinaryReader 方法将其转换为 geography 对象。 STConvexHull 是 SQL Server 2008 中包括的方法之一;您可以用它来修改 geography 或 geometry 数据的实例。 需特别注意的是,STConvexHull 仅可用于 geometry 数据类型。 SQL Server 的 geometry 和 geography 数据类型之间的区别另有详述,但目前所需要考虑的是,geometry 数据在(2 维欧几里得)笛卡尔平面中定义,而 geography 数据则使用球形坐标系统(数据、投射、本初子午线和度量单位)投射到球形地球表面。

交易区使用 geography 字段类型存储在数据库中,然后由视图转换为 varbinary。 必须将此对象读取到 geography 对象中,然后将该 geography 对象转换为 geometry 对象以运行 STConvexHull 方法。

由于涉及到的区域较小,由 STConvexHull 在(平面)geometry 对象上执行的计算,实际上与对实际球形 geography 对象的凸球面计算得到的结果相同。

在 SetLocation 的下一部分中,原始交易区以细黑线绘制,而凸球面则以粗红线呈现。 代码如图 6 所示。

图 6 绘制原始交易区和凸球面

...
// Draw trade area
if (checkBox1.Checked == false)
{
 RenderPolygon(vlocationList[curLoc].TradeAreaWKT);
}
else
{
  // Need to add C:\Program Files\Microsoft SQL
  // Server\100\SDK\Assemblies\Microsoft.SqlServer.Types.dl
  // to references
  SqlGeographyTradeAreaGeog = new SqlGeography();
  using (var stream = new
   System.IO.MemoryStream(vlocationList[curLoc].TradeArea))
  {
   using (var rdr = new System.IO.BinaryReader(stream))
   {
    TradeAreaGeog.Read(rdr);
   }
  }
  SqlGeometry TAConvexHullGeom = new SqlGeometry();
  TAConvexHullGeom =  
   SqlGeometry.STPolyFromText(TradeAreaGeog.STAsText(), 4326);
  TAConvexHullGeom = TAConvexHullGeom.STConvexHull();
  RenderPolygon(TradeAreaGeom.ToString(), 3355443, 0); // Gray80
  RenderPolygon(TAConvexHullGeog.ToString());
}
...

那么,此 WKT 字符串外观如何?RenderPolygon 对其采取什么操作? 您已看到了结果(在图 2 3 中)。 接下来进行深入讨论。

WKT 是一种开放地理空间联盟 (OGC) 标准格式,用于格式化文本格式的地理空间矢量数据。 WKT 多边形字符串外观如下所示(已大幅简化):

POLYGON ((-111.918823979795 33.6180476378649, -111.91810682416 33.6096635553986, -111.911686453968 33.6078672297299, -111.907403888181 33.599476357922, -111.907403888181 33.6060674674809, -111.903121406212 33.6060674674809))

单词“POLYGON”位于使用两组括号括起的坐标列表之前。 单个坐标对之间使用逗号分隔。 使用 MapPoint AddPolyLine 方法在地图上绘制多边形,并将其添加到 MapPoint 形状集合中。 这会将 MapPoint Location 对象的数组用作参数。 将 WKT 字符串转换为 Location 对象数组需要六行代码。 为此,RenderPolygon 将除去“POLYGON”前缀和括号,然后使用逗号分隔符将字符串拆分为坐标。 接下来将单个坐标解析为一对用于创建 MapPoint Location 对象的双精度数(经度,纬度)。 然后将生成的 Location 对象数组传递到 AddPolyline 以创建新多边形。

RenderPolygon 带有用于描述颜色和线条粗细的其他参数(参见图 7)。

图 7 RenderPolygon 方法

private void RenderPolygon(string polystring, 
 int forecolor = 128, int weight = 3)
{
  polystring = polystring.Replace("POLYGON ((", "");
  polystring = polystring.Replace("))", "");
  string[] stringList = polystring.Split(',');
  MapPoint.Location[] objLoc = 
   new MapPoint.Location[stringList.Count()];
  for (int i = 0; i <stringList.Count(); i++)
  {
   string[] coords = stringList[i].Trim().Split(' ');
   objLoc[i] = objMap.GetLocation(Convert.ToDouble(coords[1]),  
    Convert.ToDouble(coords[0]), 0);
  }
  MapPoint.Shape objShape;
  objShape = objMap.Shapes.AddPolyline(objLoc);
  objShape.Line.ForeColor = forecolor;
  objShape.Line.Weight = weight;
}

更完整的 RenderPolygon 可能会需要使用其他参数,这取决于是否填充形状、填充颜色、形状名称(可以分配到形状的内部字符串)和 zOrder(将形状定位到道路和其他形状之前或之后)。

用户和程序都可以在 MapPoint 地图上放置图形和注释。 MapPoint 总共支持使用 40 种不同的颜色进行注释。 虽然编程接口支持标准的 3 字节 RGB(16,777,216 种)颜色,实际上这些数字仅提供了指定要使用的颜色的有用方式。 有关 MapPoint 支持的 40 种颜色,请参见 mapping-tools.com/info/pushpins/colors.shtml

以前,这种限制有助于高效更新图像,但现在主要用于帮助确保颜色各不相同,从而提高地图清晰度。

我们现在来介绍 SetLocation 的最后一部分(参见图 8)。

图 8 SetLocation 的最后一部分

...
// Reset zoom level
   objMap.Altitude = 30;
   objPin.Highlight = true;

   Double distance;
   distance = 
     NearestLocation(curLoc) * 0.000621371192; //convert to miles

   label1.Text = "ID: " + locationList[curLoc].LocID.ToString();
   label2.Text = locationList[curLoc].Name + " - " +  
     locationList[curLoc].Format;
   label3.Text = locationList[curLoc].Address + ", " + 
     locationList[curLoc].City + ", " + locationList[curLoc].State;
   label4.Text = "Distance to next closest store: " + 
     String.Format("{0:#,0.0}", distance) + " miles";

  }

private double NearestLocation(int curLoc)
{

 SqlGeography AllLocations = new SqlGeography();
  SqlGeography CurLocation = new SqlGeography();
  for (int i = 0; i <locationList.Count; i++)
  {
    SqlGeography TempLocation = new SqlGeography();
    using (var stream = new 
     System.IO.MemoryStream(vlocationList[i].Location))
   { 
     using (var rdr = new System.IO.BinaryReader(stream))
     {
       TempLocation.Read(rdr);
     }
   }
   if (i == curLoc)
   {
     CurLocation = TempLocation;
   }
   else
   {
     AllLocations = AllLocations.STUnion(TempLocation);
   }
  }
  return (Double)AllLocations.STDistance(CurLocation); //meters
}

这突出了新图钉、设置缩放级别(使用 Map 对象的“高度”属性),报告店铺信息(从 locationList 数组)并查找与最近店铺位置的距离。

此距离由 NearestLocation 计算。 这将遍历位置并使用 SQL Server Spatial STUnion 方法将 Location geography 点结合到 MultiPoint geography 实例中。 当前店铺位置为例外,将跳过,否则距离始终为零英里! 应用程序然后使用 STDistance 方法计算当前店铺位置与 MultiPoint geography 实例间的距离(米)。 STDistance 报告的与某个 MultiPoint 的距离,是与 MultiPoint 中任意组件点的最短距离。

用于添加新地点位置的按钮将从地图中删除任意交易区多边形,然后将鼠标指针更改为十字光标:

private void button1_Click(object sender, EventArgs e)
{
  // Clear all previous shapes
  while(objMap.Shapes.Count > 0)
  {
    objMap.Shapes[1].Delete();
  }
  MPctrl.MousePointer = MapPoint.GeoPointer.geoPointerCrosshair;
}

为了处理 MapPoint 事件,窗体要求在窗体设计器中定义事件处理程序。 可以使用窗体设计器添加事件,也可以手动将其添加到 Map.Designer.cs。 处理程序将被添加到两个 MapPoint 事件中:SelectionChange 和 BeforeClick,如图 9 所示。

图 9 将处理程序添加到 MapPoint 事件

// 
// MPctrl
// 
this.MPctrl.Enabled = true;
this.MPctrl.Location = new System.Drawing.Point(13, 13);
this.MPctrl.Name = "MPctrl";
this.MPctrl.OcxState = 
  ((System.Windows.Forms.AxHost.State)
  (resources.GetObject("MPctrl.OcxState")));
this.MPctrl.Size = new System.Drawing.Size(674, 389);
this.MPctrl.TabIndex = 0;
this.MPctrl.SelectionChange += 
  new AxMapPoint._IMappointCtrlEvents_SelectionChangeEventHandler
 (this.MPctrl_SelectionChange);
this.MPctrl.BeforeClick += 
  new AxMapPoint._IMappointCtrlEvents_BeforeClickEventHandler
  (this.MPctrl_BeforeClick);

SelectionChange 事件用于检测用户是否选择了图钉。 这可以随后用于将当前记录移到此图钉记录。 图 10 展示了事件处理程序的实现。

图 10 实现 SelectionChange 事件处理程序

private void MPctrl_SelectionChange(object sender,  
 AxMapPoint._IMappointCtrlEvents_SelectionChangeEvent e)

{
  // Has the user just selected a pushpin?
if (e.pNewSelection is MapPoint.Pushpin)
  {
    MapPoint.Pushpin ppin = e.pNewSelection as MapPoint.Pushpin;

    // Find the corresponding location object, and select it
    for (int iloc = 0; iloc < locationList.Count; iloc++)
    {
      if (locationList[iloc].Name == ppin.Name)
      { // Found it: select, and move to it
        SetLocation(iloc);
        break;
      } 
    } 
  } 
}

这将检查新选中的对象,在本例中为图钉。 然后可以对本地 locationList 执行简单搜索以查找匹配记录。 MapPoint 图钉可以具有重复名称,因此,此代码假定所有位置记录(从而也就是图钉)具有唯一名称。 如果不能依靠此方法,还可以比较地理坐标。

地图的 BeforeClick 事件处理程序在“添加新店铺位置”功能中使用。 处理程序进行检查以确定鼠标指针是否为十字光标,即用户是否尝试插入新地点位置。 这样,MapPoint 在指针不是十字光标时仍处理单击事件。 如果鼠标指针是十字光标,则程序捕获单击操作,并在鼠标指针位置使用红色餐厅符号添加新图钉。 此时,为了简化过程,用户不必绘制交易区,程序将使用 MapPoint AddDrivetimeZone 方法围绕新地点生成假设(基于驾车时间)交易区。

为了将此形状移到 SQL Server 中,首先将形状分解为顶点,然后可以将顶点转换为多边形 WKT(文本)定义。 这会随后写入 SQL Server。

要将点和多边形传递回 SQL Server 并更新 geography 列,不能使用实体框架支持的普通存储过程,这是因为它们不支持 geography 数据类型。 但是,由于 Entity Framework 4.0 现在支持执行任意存储过程,我们可以将存储过程导入并作为普通函数执行。

此代码将设置参数,然后执行 uspAddLocation 存储过程:

object[] parameters =
{   
  new SqlParameter("Latitude",objLoc.Latitude),
  new SqlParameter("Longitude",objLoc.Longitude),
  new SqlParameter("PolyWKT",PolyWKT)
};

var retValue = db.uspAddLocation(objLoc.Longitude, 
 objLoc.Latitude, PolyWKT);

最后,此例程将重置地图 (CreateNewMapObject)、从数据库重新查询位置列表 (InitMapPins) 并选择新店铺作为当前记录 (SetLocation):

// Re-query and re-initialize map
 ObjectQuery<Location> locationQuery = db.Locations;
 ObjectQuery<vLocation> vlocationQuery = db.vLocations;
 locationList = locationQuery.ToList();
 vlocationList = vlocationQuery.ToList();
 objMap.Saved = true;
 CreateNewMapObject();
 InitMapSymb();
 InitMapPins();
 SetLocation( locationList.Count – 1 );
 e.cancel = true;
}

行 e.cancel=true; 防止 MapPoint 进一步处理单击事件。 后面是存储过程 uspAddLocation(参见图 11)。

图 11 uspAddLocation 存储过程

CREATE PROCEDURE [dbo].[uspAddLocation]
@Longitude FLOAT,
@Latitude FLOAT,
@PolyWKT NVARCHAR(MAX)
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
SET NOCOUNT ON;

DECLARE @NewLocID int = 0
SELECT @NewLocID = MAX(LocID)+1 FROM Locations

DECLARE @NewPoly geography
SET @NewPoly = geography::STPolyFromText(@PolyWKT,4326)

INSERT INTO Locations(LocID,Name,Address,City,State,Format,Location,TradeArea)
VALUES(@NewLocID, 'New Location ' + CAST(@NewLocID As varchar(3)), '123 Main',   
 'Anywhere', 'ST', 'Food', geography::Point(@Latitude,@Longitude,4326), 
 @NewPoly)

SELECT @NewLocID AS NewLocID

END

您可以查看 INSERT 语句之前为多边形创建的新 geography 实例和变量,其中 Point 位置通过 INSERT 方法内部的 Point 方法创建。 两种方法均有效。

图 12 中的事件处理代码处理“上一个”和“下一个”按钮、凸球面复选框和窗体关闭,后者将完成应用程序。

图 12 完成应用程序

private void prev_Click(object sender, EventArgs e)
{
  IncDecLocation(-1);
}

private void next_Click(object sender, EventArgs e)
{
  IncDecLocation(1);
}

private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
  IncDecLocation();
}

private void Map_FormClosing(object sender, FormClosingEventArgs e)
{
  db.Dispose();
  MPctrl.ActiveMap.Saved = true;
}

查看和直观编辑 SQL Server 地理空间数据

本文涵盖甚广,其中演示了 SQL Server 中完整的端到端应用程序,展示了如何使用 SQL Server Spatial 和 MapPoint 方法往返传递数据以显示和编辑实际信息。

此处演示的原则可以进一步深入。 请保留一份本地数据副本以确保快速更新地图,但对于海量数据集而言,这并不现实。 在这些情况下,应该逐个记录提取数据,最好使用缓存机制。

MapPoint 可以执行一些有用的地理空间操作(例如,驾车时间区域计算),但对于完整 GIS 所需的许多地理空间操作则无能为力。 本文中使用了两个来自 SQL Server Spatial 扩展程序的此类操作:STConvexHull 和 STDistance。 空间扩展程序中提供的其他高级功能包括测量地理特征的长度和宽度以及查找多边形的并集和交集。 这些功能可用于创建复杂的地域管理应用程序。 这可以组合地域或查找存在店铺同业竞争的重叠情况。

与此类似,也可以利用 MapPoint 的优势。 MapPoint 能够进行脱机地理编码。 本例中使用现有坐标,但 MapPoint 地理编码器也可用于定位街道地址。 MapPoint 中还提供了多种县、邮政编码和人口普查地域级别的人口统计数据库。 这些数据可以绘制在店铺地图上,从而可以方便地进行比较,例如,店铺销售额与当地人口和收入水平的比较情况。

展望未来,SQL Server Spatial 有望在 SQL Server 的下一版本中实现跨越式提升,MapPoint 产品则通过在最近的两个版本中实现新的开发和功能而得以复兴,并且这种趋势仍将持续下去。 此外,实体框架将继续增加对新字段类型的支持,包括空间数据类型,这将改进 SQL Server 与 MapPoint 之间的通信。 总而言之,这些技术构成了用于开发地图应用程序的可靠、强大且不断发展的平台。

Eric Frost 是 Microsoft MVP 和业务应用程序开发人员,专注于 GIS/地图应用程序。他管理着活跃的 Microsoft 地图技术论坛 mapforums.com,联系方式如下:eric.frost@mp2kmag.com

Richard Marsden 是 Microsoft MVP 和兼职软件开发人员。他在 mapping-tools.com 上出售多种 MapPoint 扩展程序并运营了一个 GeoWeb Guru 网站,网址为:geowebguru.com

衷心感谢以下技术专家对本文的审阅: Bob Beauchemin , Ed Katibah Amar Nityananda