VC+MO 实现线地图取点坐标

来源:互联网 发布:淘宝的个人主页在哪里 编辑:程序博客网 时间:2024/04/27 17:15
好久没写文章了,可能是懒的缘故.

最近在搞毕设,忙得头晕,之前在Linux下玩得爽,懒得去学VC,这次不幸用到了,着实郁闷.
言归正传,我们的毕设做的是利用一种称为R-tree的数据结构去索引二维平面中的物体.而其索引的依据则是物体的坐标.但是我们所得到的一张

USA加州的公路网图却只有线图层,也就是说,整幅地图中,只有线,却没有点,而要去索引这些路段,我们就必须得到每条路段的起始与终止点坐标

才可以.因此,我必须利用MO去得到每条线段的起始终止点坐标.

如何去取呢.我的方法是取到图中的每一条线,然后去取线的两端点,最后取坐标.看起来比较简单,要做的话就有点麻烦了

首先是MO的安装,我是在天网上搜到MapObjects2的安装包的,下到后安装一下就可以了.下载的地址记不大清了
装好MO后,我们可以在一个MFC工程中添加ActiveX控件加入MO控件.在VC的"工程"->"添加工程"->"Component and controls",然后选择

MapObjects2即可,这时你会发现你的控件工具箱里就会多出一个地图控件MapObjects2.Map来,其对象类型为CMap1

将地图控件拖到Form中去,设置属性,加入地图文件 shp,运行程序,即可在程序中显示地图,跟ArcView一样.

当然,我们的想法是能够动态地去显示地图,而非如此生硬地指定某一地图文件即可.因此,我们的程序就可以这样:
我们采用的MFC是FormView,在MainForm中插入地图控件m_map后,在View类中加入几个私有变量:
    CMoDataConnection conn;
    CString path;
    CMoMapLayer layer;
其中,CMoDataConnection是负责数据连接的,shp文件的打开同数据库类似,必须先做连接,再做记录集等等才可,这点我们以后再讲
path是文件路径,layer是指m_map中的一个图层,用来添加一幅地图
在定义这些变量后,我们在OnInitialUpdate中加入如下语句:

    path="map//part2.shp";
    //建立数据连接...
    conn.CreateDispatch(TEXT("MapObjects2.DataConnection"));
    conn.SetDatabase(GetFileDirectory(path));
    if(!conn.Connect())
        throw new CFileException(CFileException::fileNotFound);

    //连接图层...
    layer.CreateDispatch(TEXT("MapObjects2.MapLayer"));
    CMoLayers layers(m_map.GetLayers());
    CMoGeoDataset geoDataset(conn.FindGeoDataset(GetFileTitle(path)));
    layer.SetGeoDataset(geoDataset);

    //加入图层...
    layers.Add(layer);

就如前面所讲的那样,打开shp文件与数据库类似,首先建立连接,然后连接图层,即具体地图,
由语句CMoGeoDataset geoDataset(conn.FindGeoDataset(GetFileTitle(path)))所完成,最后,由地图控件的图层集合加入此图层即可

其中,GetFileTitle()等函数都是自定义的,如下所示:

CString CMO_T6View::GetFileDirectory(const CString& path){
    ASSERT(path.GetLength());
    int pos = path.ReverseFind('//');
    if(pos>=0)
        return path.Left(pos);
    return "";
}

//-----------------------------------------------------------
CString CMO_T6View::GetFileName(const CString& path){
    ASSERT(path.GetLength());
    int pos=path.ReverseFind('//');
    if (pos >=0)
        return path.Right(path.GetLength()-pos-1);
    return "";
}

//-----------------------------------------------------------
CString CMO_T6View::GetFileTitle(const CString& path){
    ASSERT(path.GetLength());
    CString strResult= GetFileName(path);
    int pos=strResult.ReverseFind('.');
    if(pos>=0)
        return strResult.Left(pos);
    return strResult;
}

设置好图层后,我们就可以动态地加入地图文件,只要设定文件路径即可
全部设置妥当后,接下去就是我们的核心部分,取点.我利用加入菜单项进行操作,程序如下:

    //取得数据集...
    CMoRecordset recs(layer.GetRecords());
    CMoFields fields(recs.GetFields());
    CMoField _IDfield(fields.Item(COleVariant( TEXT("OBJECTID") ) ));

    //查询用变量...
    CMoRecordset searRecs;
    CMoFields searFields;
    searFields.CreateDispatch(TEXT("MapObjects2.Fields"));
    CMoField  searField;
    searField.CreateDispatch(TEXT("MapObjects2.Field"));

    FILE *fp=fopen(GetFileTitle(path),"w");
    CString ObjectID;
    CString expression,result;
    float x,y;
    long i=0,j=0;
    while(!recs.GetEof()){
        //输出数据...
        ObjectID=_IDfield.GetValueAsString();
        m_list.InsertItem(i,ObjectID);

        fromNode=_FNfield.GetValueAsString();
        m_list.SetItemText(i,1,fromNode);

        toNode=_TNfield.GetValueAsString();
        m_list.SetItemText(i,3,toNode);

        expression="Objectid=";
        expression+=ObjectID;
        expression+="";

        //取到线段.....取形状类一率用Shape
        searRecs=layer.SearchExpression(expression);
        searFields=searRecs.GetFields();
        searField=searFields.Item(COleVariant(TEXT("Shape")));
        CMoLine line;
        line.AttachDispatch(searField.GetValue().pdispVal);

        CMoParts parts=line.GetParts();
        CMoPoints pts;

        VARIANT psItem;
                psItem.vt=VT_I4;
        psItem.lVal=0;

        pts.AttachDispatch(parts.Item(psItem));
    
        CMoPoint pt;
        VARIANT pItem;
        pItem.vt=VT_I4;
        for(j=0;j<pts.GetCount();j++){
            pItem.lVal=j;
            pt=pts.Item(pItem);
            x=pt.GetX();
            y=pt.GetY();
            result.Format("%f %f",x,y);
            if(j==0){
                fprintf(fp,"%d %f %f",i,x,y);
                m_list.SetItemText(i,2,result);
            }
            else if(j==pts.GetCount()-1){
                fprintf(fp," %f %f/n",x,y);
                m_list.SetItemText(i,4,result);
                i++;
            }
            else{
                fprintf(fp," %f %f/n",x,y);
                m_list.SetItemText(i,4,result);
                i++;
                m_list.InsertItem(i,ObjectID);
                fprintf(fp,"%d %f %f",i,x,y);
                m_list.SetItemText(i,2,result);
            }
        }    
        recs.MoveNext();
    }
    fclose(fp);
此程序通过查询数据库中的ID字段,取到地图中的每条线段,由于线段可能是折线,因此取到线中的每个点,两个点一对地置入列表控件m_list并

写入文件fp中去.
(以上程序因需要,截去部分,但大部分内容还是保留的)
查询字段值通过以下三句完成:
    CMoRecordset recs(layer.GetRecords());
    CMoFields fields(recs.GetFields());
    CMoField _IDfield(fields.Item(COleVariant( TEXT("OBJECTID") ) ));
先取到记录集recs,根据记录集取到列集合fields,再依据字段名取到具体字段
while(!recs.GetEof()){
    ObjectID=_IDfield.GetValueAsString();
    ...
    recs.MoveNext();
}
利用以上语句取出相应字段值.
封装查询语句expression:
    expression="Objectid=";
    expression+=ObjectID;
    expression+="";
通过查询语句取到直线,并赋到相应直线对象中去
    searRecs=layer.SearchExpression(expression);
    searFields=searRecs.GetFields();
    searField=searFields.Item(COleVariant(TEXT("Shape")));
    CMoLine line;
    line.AttachDispatch(searField.GetValue().pdispVal);
其中,searField=searFields.Item(COleVariant(TEXT("Shape")))做的是通过Shape关键字,取到图形元素
而line通过AttachDispatch取到searField的图形元素对象

取到线段后,通过line->parts->points->point的顺序取点,各类型之间是集合关系,通过Item方法取值,具体可参见代码


东西做了很久,终于算是比较顺利地完成了.对MO和MFC都不熟,之所以能做出来,非常感谢网上好友的支持.

原创粉丝点击