这几天,在客户那里做Virtual Earth的技术支持,需要帮助客户基于ASP.NET 3.0+Javascript+WCF+Silverlight+Ado.net data service等技术上,做地图应用,其中有一个比较有意思的问题是,自定义Shape对象的图标,几经摸索最终搞定。
场景描述:
用户在使用程序的时候,需要添加图钉(Pushpin)、线条(Polyline)、多边形(Polygon)等到数据库里面,然后可以通过一个按钮或者其他的方式,把这些数据读取出来,显示在地图上。对于这里提到的几个图形(Shape),需要使用自定义的图标、线条及其他描述信息。
根据MS的VE的SDK里面的文档,把这些Shape添加到数据库中,可以非常的容易实现,而且在添加的时候也容易把各种自定义的图标赋值给Shape上,也容易存储到数据库中,遇到的问题是如何取出来,并且显示到地图上去。
实现方式:
一、VEMap.ImportShapeLayerData
首先,找到Virtual Earth的SDK(http://dev.live.com/virtualearth/sdk),然后里面提供了一个“Import data into shape layers”栏目,用于把成批的数据导入到地图上去,看上去用这里提供的方法可以帮助我们去完成这个要求。
在VirtualEarth里面提供一个名为ImportShapeLayerData的方法来加入成批的数据,格式如下:
VEMap.ImportShapeLayerData(shapeSource, callback, setBestView); |
其中shapeSource可以是GeoRSS、ImportXML、VECollection,按照官方的例子,我们选择了GeoRSS的类型,通过ADO.NET DataService,生成了一个GeoRSS的文档,并且传递给这个方法,用来显示的我们的全部数据。代码如下:
function btnPolygonId_Click() { |
var request = ""; |
if (document.getElementById("txtID").value != "") { |
request = "RetrieveService.svc/GetPolygon/" + document.getElementById("txtID").value; |
} |
else { |
var num = Math.random(); |
var request = "RetrieveService.svc/GetPolygon/all?num=" + num; |
} |
var layer = new VEShapeLayer(); |
var layerSpec = new VEShapeSourceSpecification( |
VEDataType.GeoRSS, |
request, |
layer); |
map.ImportShapeLayerData(layerSpec, onfeedload, 0); |
} |
function onFeedLoad(feed) |
{ |
alert('RSS or Collection loaded. There are '+feed.GetShapeCount()+ |
' items in this list.'); |
} |
这时,我们去测试他,发现能够显示出来数据,但是里面自定义的图标则没有办法显示,而查看我们的服务,该服务能够数据是取正确了的,而且是符合GeoRSS的格式要求的。没有显示出来,只有求助google和baidu了,终于发现了一个帖子,是说的我们能够去在onFeedLoad事件中,可以自定义图标,代码如下:
function onfeedload(layer) |
{ |
var numShapes = layer.GetShapeCount(); |
var s, n, icon; |
for(var i = 0; i < numShapes; ++i) |
{ |
s = layer.GetShapeByIndex(i); |
n = i + 1; |
icon = "<img src='orange_pushpin.png'><span class='pinText'>" + " " + n + "</span>"; |
s.SetCustomIcon(icon); |
} |
} |
这时,我们能够设置一个自定义的图标给我们的Shape,但是有个局限,所有的shape只有一个对象,郁闷之极。
二、VEMap.AddShape
在上面的思路出现问题后,只有想其他办法。在做Shape的添加的时候,我们发现每个Shape的自定义属性都能够输入,那么我们可以把所有的Shape取出来,然后一个个都添加到地图上去,那么我们添加的时候,就可以去控制图形的属性了。
于是,而且这个思路在Windows Live Tool For Virtual Earth中,已经测试是可以的,于是我们往这个方向去做,实现的代码如下:
function GetServerDT_CallBack(datatable) |
{ |
map.DeleteAllShapes(); |
var layer = new VEShapeLayer(); |
layer.SetTitle("layer01 title"); |
map.AddShapeLayer(layer); |
var allpoints = new Array(); |
var xmlDoc = loadXML(datatable.value); |
var shapes = new Array(); |
var cnode = xmlDoc.selectNodes("//NewDataSet/Table"); |
for (var i = 0; i < cnode.length; i++) |
{ |
var id = cnode[i].childNodes.item(0).text; |
var title = cnode[i].childNodes.item(1).text; |
var descption = cnode[i].childNodes.item(2).text; |
var polyline = cnode[i].childNodes.item(3).text; |
var linkurl = cnode[i].childNodes.item(4).text; |
var imgicon = cnode[i].childNodes.item(5).text; |
var indate = cnode[i].childNodes.item(6).text; |
var linepoints = polyline.split(" "); |
var thepoints = new Array(); |
for (var j = 0; j < linepoints.length / 2; j++) |
{ |
var m = j * 2; |
var n = m + 1; |
var arr = new VELatLong(); |
arr.Latitude = linepoints[m]; |
arr.Longitude = linepoints[n]; |
thepoints[j] = arr; |
} |
var shape= new VEShape(VEShapeType.Polyline, thepoints); |
shape.SetTitle("Enter a name to save"); |
shape.SetCustomIcon("images/target.gif"); |
shape.SetDescription(descption); |
layer.AddShape(shape); |
} |
map.SetMapView(allpoints); |
} |
这个时候,代码调试正常后,运行结果发现,如果所有的图形是Pushpin时,是可以添加到地图上,如果是其他的如Polyline和Polygon时,则不能够添加到地图上,每次一循环时,把第一个Shape添加到地图上后,就出现了未处理的异常,估计是浏览器已经在写数据了,而这个时候循环还没有执行完成,矛盾了。于是这个思路也死掉了。
三、VEMap.ImportShapeLayerData
于是,整个项目降低要求,自定义Shape图标的功能暂时不考虑,先把成批数据取出来后,把其他功能实现了再说,好吧,我们回到了VEMap.ImportShapeLayerData 这个方法上面去,把数据显示出来,只是每个图标都是那个大大红红的图钉。
后来在休息的时候,我尝试去看看这个Shape对象究竟是怎么回事的时候,在VEMap.ImportShapeLayerData 的onFeedLoad处理程序中,跟踪了一下获取到的Shape对象,发现了一个有趣的事情,Shape对象有两个图标相关的属性一个是“IconId”,另外一个是“IconUrl”, 我们传递过来的自定义Shape图标的数据事实上已经存在“IconID”里面,而另外一个“IconUrl”则是VE的默认图钉的地址。唉,原来这样啊,这下子比较简单了,于是在onFeedLoad事件中,重新写了一下。
function onfeedload(layer) { |
var numShapes = layer.GetShapeCount(); |
for (var i = 0; i < numShapes; ++i) { |
var s = layer.GetShapeByIndex(i); |
s.SetCustomIcon(s.IconId); |
} |
} |
呵呵,这下子工作的非常好了。
总结
其实,在VE6.2的SDK中,没有公布出来Shape对象的这些属性,所以我们通过调试时去跟踪,会发现有些可以用到的比较好的属性,
1、保存时候的,我们可以通过Shape的_customIcon 属性来获取自定义图标的值,代码“var icon = map.GetShapeByID(shapeId)._customIcon ”;
2、获取的时候,可以通过本文描述的Shape的IconId来获取
最后一句话,不要太依赖SDK中已经公布的东西了,要多进入里面去看看,说不定“柳暗花明又一村”。