在数据库中,你可以选择Geography类型来存储地理数据类型,这种类型会判断方向(以顺时针为准,如果你逆时针画多边形的话,实际上算的是这个多边形以外的全部地球,这样就太大了),而Geometry类型是不会判断方向的,它计算距离时也只计算平面距离,Geography计算球面距离。在此我仅介绍用Geometry。
因为Entity Framework不支持地理数据类型,所以用一般的数据库操作不可以把geography和geometry数据类型读出和写入。在数据库中写存储过程时要做相应转换处理,外面 的代码也有相关的操作。
插入
字段Location的数据类型为geometry。注意到select * from BingMap,如果没有这句,在调用此存储过程时会报错“存储区数据提供程序返回的数据读取器所具有的列数对于所请求的查询不够。”,所以select这句必不可少。
if exists(select * from sys.objects so
where so.object_id=object_id(N'InsertBingMap')
and so.type in (N'P'))
drop procedure [dbo].[InsertBingMap]
go
create procedure [dbo].[InsertBingMap]
(@Name nvarchar(50), @GeoType nvarchar(50),
@Locations nvarchar(max), @Latitude decimal(18,12),
@Longitude decimal(18,12), @Radius decimal(18,12))
as
insert into BingMap(Name,GeoType, Locations,Latitude,Longitude,Radius)
values(@Name, @GeoType, geometry::STPolyFromText(@Locations,4326),@Latitude, @Longitude, @Radius)
select * from BingMap
go
很明显,在C#或Js代码中,传进来的地理数据类型为字符串,这种字符串要遵循一定规则,如下为js代码实例:
function parsePointsToStr(locs) {
//locs为Microsoft.Maps.Location()类型数组Array()
var len = locs.length;
var locsStr = "POLYGON((";
for (var i = len - 1; i >= 0; i--) {
locsStr += locs[i].longitude;
locsStr += " ";
locsStr += locs[i].latitude;
locsStr += ",";
}
locsStr = locsStr.substring(0, locsStr.length - 1);
locsStr += "))";
return locsStr;
}
获取
Entity Framework不接受地理数据类型,但接受二进制类型,所以得在数据库中把地理数据类型转换为二进制,然后建立整个表一个视图,创建查询此视图的一个存储过程即可。sql语言如下:
if exists(select * from sys.objects so
where so.object_id=object_id(N'BingMapView')
and so.type in (N'V'))
drop view BingMapView
go
create view BingMapView
as
select bm.SID, bm.Name, bm.Latitude,bm.Longitude,bm.Radius,bm.GeoType,
bm.Locations.STAsBinary() as Points from BingMap bm
go
if exists(select * from sys.objects so
where so.object_id=object_id(N'SelectBingMapView')
and so.type in (N'U'))
drop procedure [dbo].[SelectBingMapView]
go
create procedure [dbo].[SelectBingMapView]
as
select * from [dbo].[BingMap]
go
而在C#代码中,读取到此二进制数据之后,要进行转换,才可重新生成地理数据类型,进而让它成为最初我们插入时的字符串类型格式:
using (IgnitionEntities ie = new IgnitionEntities())
{
SelectBingMapView_Result mapGraph = ie.SelectBingMapView().First();
SqlGeometry geo = SqlGeometry.STGeomFromWKB(new SqlBytes(mapGraph.Points), 4326);
string locsStr = mapGraph.Points.ToString();
//to do
}
这里用到了SqlGeometry类,在类要添加一个引用,路径在C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies下的 Microsoft.SqlServer.Types.SqlGeometry,并添加using Microsoft.SqlServer.Types;命名空间,详情见http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeometry.aspx
STGeomFromWKB函数是把二进制数据转换成地理数据类型,4326是空间参考系(spatial reference ID,SRID), 然后直接.toString()就可以形成 Poly((***))这样格式的字符串。
接下来你只要解析此字符串就行了,非常容易,js代码如下:
function parseStrToLocs(str) {
str = str.split("((")[1]; //去掉前面的Polygon((
str = str.split("))")[0]; //去掉后面的))
var pts = str.split(","); //解析出经纬度字符数组
var locs = new Array();
for (var i = pts.length - 1; i >= 0; i--) {
var ps = pts[i];
if (ps.charAt(0) == " ") {
ps = ps.slice(1); //若前面有空格,则去掉
}
ps = ps.split(" ");
var loc = new Microsoft.Maps.Location(ps[1], ps[0]);
locs.push(loc);
}
return locs;
}
现在你则可以从数据库中读取地理数据类型并显示在bingMap上了,
var options = {
fillColor: new Microsoft.Maps.Color(50, 255, 0, 0),
strokeColor: new Microsoft.Maps.Color(255, 0, 0, 0)
};
var poly = new Microsoft.Maps.Polygon(locs, options);
map.entities.push(poly);
当然,如果你不用Entity Framework,可以直接把地理数据读出来,不过读出来之后,你仍然要经过一系列的转换。
用DataSet可以直接读取地理数据表中的那一列,object polygon = row["Polygon"],然后用如下代码直接读取出它的经纬度
var geo = SqlGeography.STGeomFromText( new SqlChars(new SqlString(polygon.ToString())), 4326);
for (int j = 1; j <= geo.NumRings(); j++)
{
if (geo.RingN(j).STNumPoints() > 1)
{
for (int k = 1; k <= geo.RingN(j).STNumPoints(); k++)
{
double latitude = (double)geo.RingN(j).STPointN(k).Lat,
double longitude = (double)geo.RingN(j).STPointN(k).Long));
}
}
}
读者可以参考以下的资料和博客:
http://msdn.microsoft.com/en-us/library/cc280487.aspx
http://www.simple-talk.com/dotnet/asp.net/mapping-your-data-with-bing-maps-and-sql-server-2008/
http://database.51cto.com/art/201103/250138.htm