这几天,在客户那里做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中已经公布的东西了,要多进入里面去看看,说不定“柳暗花明又一村”。

posted on 2008-11-14 16:38  月华  阅读(741)  评论(1编辑  收藏  举报