在经过第一篇的简单学习之后,我们开始了解一些稍微有点儿意思的东西,进一步掌握和学习利用sharpmap进行开发的技巧。
这次,我们主要是跟大家一起学习一下如何根据地图上的一个点,来查询这个点所在的对象的信息,并显示到点击的位置。这非常有用,比如说一个想把一个房子显示在地图上,我们用鼠标一点,便知道这个房子里住的什么人,干什么的,以及其它相关信息。
同样的,我们还是使用sharpmap提供的ajax控件,环境和第一篇一模一样。但是这里,我们要引用一个叫做NetTopologySuite的类库。它经常和SharpMap一起使用,这次我们只使用其中的一个小部分,废话不多说,开始做。
这里我们使用ASP.net ajax 1.0,首先引用了dll,并且拖上scripmanager并设置为EnablePageMethods=true,这样我们就可以在页面中写静态方法来实现AJAX。
在MapClicked方法(AjaxMapControl控件提供的方法,直接写在js中即可,表示单击的时候发生一些事情)中,我们调用我们写的js,根据两个点来返回一个字符串。这个字符串就是拼好的html,直接显示出来。
function MapClicked(event,obj)
{
var mousePos = SharpMap_GetRelativePosition(event.clientX,event.clientY,obj.container);
var pnt = SharpMap_PixelToMap(mousePos.x,mousePos.y,obj);
var field = document.getElementById('dataContents');
//field.innerHTML = "You clicked map at: " + pnt.x + "," + pnt.y;
GetData(pnt.x, pnt.y);
}
这个方法里面的SharpMap_GetRelatiovePosition和 SharpMap_PixelToMap方法根据鼠标在屏幕上的坐标计算出鼠标点在地图上的坐标,再调用我们自己写的GetData方法即可。在GetData方法中,我们使用了PageMethods来调用一个后台方法,并返回一个字符串。
PageMethods.GetData(x, y, GetDataSuccess);
然后,在GetDataSuccess中调用一个写好的用来显示这些返回数据的方法(此方法来自网络,js源码如下)。
<script type="text/JavaScript">
<!--
var ns4 = document.layers;
var ns6 = document.getElementById && !document.all;
var ie4 = document.all;
offsetX = 0;
offsetY = 20;
var toolTipSTYLE = "";
function initToolTips() {
if (ns4 || ns6 || ie4) {
if (ns4) toolTipSTYLE = document.toolTipLayer;
else if (ns6) toolTipSTYLE = document.getElementById("toolTipLayer").style;
else if (ie4) toolTipSTYLE = document.all.toolTipLayer.style;
if (ns4) document.captureEvents(Event.MOUSEMOVE);
else {
toolTipSTYLE.visibility = "visible";
toolTipSTYLE.display = "none";
}
document.onclick = moveToMouseLoc;
}
}
function toolTip(msg, fg, bg) {
if (toolTip.arguments.length < 1) // hide
{
if (ns4) toolTipSTYLE.visibility = "hidden";
else toolTipSTYLE.display = "none";
}
else // show
{
if (!fg) fg = "#777777"; //fore color
if (!bg) bg = "#FFFFFF"; //bg color
var content = msg;
if (ns4) {
toolTipSTYLE.document.write(content);
toolTipSTYLE.document.close();
toolTipSTYLE.visibility = "visible";
}
if (ns6) {
document.getElementById("toolTipLayer").innerHTML = content;
toolTipSTYLE.display = 'block'
}
if (ie4) {
document.all("toolTipLayer").innerHTML = content;
toolTipSTYLE.display = 'block'
}
}
}
function moveToMouseLoc(e) {
if (ns4 || ns6) {
x = e.pageX;
y = e.pageY;
}
else {
x = event.x + document.body.scrollLeft;
y = event.y + document.body.scrollTop;
}
toolTipSTYLE.left = x + offsetX;
toolTipSTYLE.top = y + offsetY;
return true;
}
//-->
</script>
需要指出的是,这个效果需要做一些初始化的工作:
<div id="toolTipLayer" style="position:absolute;visibility: hidden;z-index:10000">
</div>
<script type="text/JavaScript">
initToolTips();
</script>
这样,我们在前台的工作就基本完成了。
当然,如果想要更好看一些,再写一个css帮助从后台传来的html进行淡化。
.clarity
{
background-color:#4d7d99;
filter:alpha(opacity=80,Style=0);
}
OK,现在转去后台。
在根据两个点查询对象数据时,我们首先要初始化需要初始化shapefile,也就是你要查询的那个层的数据源。
new SharpMap.Data.Providers.ShapeFile(filepath true);
然后遍历shapefile里面的每一个feature,对比传入的点和feature所在的Geometry,如果传入的点在feature所在Geometry之内(within方法)的话,就读取当前feature的一个attribute(通常是某个业务对象的ID),这样,根据这个ID就能取到业务对象的值,然后Build一下HTML,返回就OK了。主要代码如下:
public string GetInfo(double x, double y)
{
SharpMap.Data.Providers.ShapeFile oShape = (SharpMap.Data.Providers.ShapeFile)GetShapeFile(filepath);
if (!oShape.IsOpen)
{
oShape.Open();
}
uint iFeature = (uint)oShape.GetFeatureCount();
SharpMap.Geometries.Point oPoint = new SharpMap.Geometries.Point(x, y);
GISSharPBlog.netTopologySuite.Geometries.Geometry oPt = SharpMap.Converters.NTS.GeometryConverter.ToNTSGeometry(oPoint, new GisSharpBlog.NetTopologySuite.Geometries.GeometryFactory());
StringBuilder sb = new StringBuilder();
// find a record
string graveInfo = string.Empty;
for (uint i = 0; i < iFeature; i++)
{
GisSharpBlog.NetTopologySuite.Geometries.Geometry oPoly = SharpMap.Converters.NTS.GeometryConverter.ToNTSGeometry(oShape.GetFeature(i).Geometry, new GisSharpBlog.NetTopologySuite.Geometries.GeometryFactory());
if (oPt.Within(oPoly))
{
if (oShape.GetFeature(i)[someId].ToString().Length > 0)
{
return BuildGraveInfoTable(name, value);
}
}
}
return string.emtpy;
}
这里把sharpfile的Geometry都转换成了NTS 的,这样,使用NTS的Within方法进行比较。sharpmap自己的方法在我的测试中是不能用的,希望大家在使用过程中做些尝试,看看是否是我自己的代码有问题,总之,我是用的NTS。
到这里,我们就实现了根据点来查询对象数据的功能,我们只需要在shapefile中存储一个attribute(比如对象的ID),然后取出来显示出去就OK了。
同理的,我们也可以实现根据一个用户ID来找到这个对象在地图中的位置,并显示在地图中间。
代码如下:
public SharpMap.Geometries.Point GetPointByID(string Id)
{
SharpMap.Data.Providers.ShapeFile oShape = (SharpMap.Data.Providers.ShapeFile)GetShapeFile(filePath);
oShape.Open();
uint i = 0;
uint iFeature = (uint)oShape.GetFeatureCount();
SharpMap.Geometries.Point oPt = null;
for (i = 0; i < iFeature; i++)
{
GISSharPBlog.netTopologySuite.Geometries.Geometry oPoly = SharpMap.Converters.NTS.GeometryConverter.ToNTSGeometry(oShape.GetFeature(i).Geometry, new GisSharpBlog.NetTopologySuite.Geometries.GeometryFactory());
if (oShape.GetFeature(i)["Id"].ToString() == Id)
{
oPt = new SharpMap.Geometries.Point(oPoly.InteriorPoint.X, oPoly.InteriorPoint.Y);
}
}
return oPt;
}
找到这个点之后,设置一下控件的地图的中心点,就OK了。
ajaxMap.Map.Center = pCenter;
虽然东西很简单,但是还是费了我很多功夫。很大一部分原因是因为sharpmap自己提供的方法不太完善,导致很多时间花费在了追踪问题上。不过这种追踪对理解开源的代码也是很有好处的。
在这个例子中,我使用了遍历地图中所有feature的方法来定位一个对象,这样非常的耗费资源,效率并不好。如果地图对象比较少还可以,一旦超过一定的数量级,可能会出现性能问题。以后我会在这个基础上进行改进,利用其它方法来实现这个功能(事实上我已经进行了一些尝试,只是还没有成功。如果有人成功了,希望给我一些建议,谢谢)。
今天就到这里,感谢大家的关注,希望多多拍砖,共同进步。
在下一篇中,我将和大家共同学习关于根据attribute的值来填充地图上对象的颜色,设置他们的样式以及一些有趣的小玩意儿。希望大家继续关注,谢谢。