zoukankan      html  css  js  c++  java
  • VC2010+ArcGIS Engine 10.1 开发(一)

     

     

     

      

     

     

     

     

     

      

    1. 使用MFC(VS2010)开发ArcGIS Engine 10.1

    网上C#结合ArcGIS Engine的资料简直太多了,多的都无法形容,但是C++的却很少,前一段时间不断的有人问在VC中如何开发ArcGIS Engine,说实话我几乎没怎么用过VC,在学校用过,那已经是好多年的事情了,现在重温VC,不知道会是什么样的感觉,年末了,大家都比较忙,我也是抽空,静下心来尝试的使用VC去开发,2个星期前在博客中发了一篇(http://www.gisall.com/html/63/151663-8220.html),那个是没有界面的,也就是没有用到MFC,访问量还不错,于是决定写一个MFC的小例子,界面这块,我一点都不擅长,习惯了C#中的做法,在C#中就是拖个按钮,然后直接就在下面写功能,界面是一个体力活,更是一个艺术活,像我这没有艺术细胞的人,估计这辈子都做不了漂亮的界面,所以下面的小例子,大家也就不谈论界面了,哈哈。现在言归正传,开始我们的旅程。

    1. 建立MFC工程

    在这里可以选择单文档,也可以选择基于对话框的,我选择了单文档,如下图:

    注意下面要选CFormView,默认的是CView,关于这两个的区别看中间的这个词语就知道了,如下图:

    1. 添加类库

    在工程上右键,属性找到VC目录的栏目,在包含那个选项中添加

    Engine安装目录下的com

    SDK目录下的CPPAPI

    还有Common Files\ArcGIS\bin

    因为我的目录中有x86,添加后变成这个样子了,如下图:

    在C/C++选项的预处理中添加:ESRI_WINDOWS,如下图:

    1. 引入头文件

    在stdfx.h中引入ArcSDK.h 这个目录文件,编译的时候

    会看到下面的错误(不要怕,名称冲突而已)

    两种解决办法:

    • 重命名,找到相应的头文件,在import指令后添加rename属性(关于这些属性大家可以自己搜索下)

    #include "esrisystem.h"

    #import "esrisystemui.olb" raw_interfaces_only raw_native_types no_namespace named_guids exclude( "OLE_HANDLE", "OLE_COLOR", "UINT_PTR" ) rename("ICommand", "esriICommand") rename("IProgressDialog", "esriIProgressDialog")

    • 使用全名(命名空间+接口名称)

    #include "esrisystem.h"

    #import "esrisystemui.olb" raw_interfaces_only raw_native_types named_guids exclude( "OLE_HANDLE", "OLE_COLOR", "UINT_PTR" )

    其他的依次类推,注意如果重新命名之后,在程序中应使用新的名称,不然还是会出错。

    1. 绑定许可和初始化许可,绑定许可是10.0之后的必要操作

    bool CMainFrame::AEinit(void)

    {

        

    #pragma region 绑定许可

            IArcGISVersionPtr ipVer(__uuidof(VersionManager));

            VARIANT_BOOL succeeded;

            if (FAILED(ipVer->LoadVersion(esriArcGISEngine , L"10.1",&succeeded)))

                return false;

    #pragma endregion

    #pragma region 初始化许可

            IAoInitializePtr ipInit(CLSID_AoInitialize);

            esriLicenseStatus status;

            ipInit->Initialize(esriLicenseProductCodeEngine, &status);

            if (status != esriLicenseCheckedOut)

                AoExit(0);

            return true;

    #pragma endregion

        

    }

    1. 如何显示地图

    现在头文件,初始化许可的事情都搞定了,但是地图如何显示?想象下C#是如何做的,拖一个地图控件上去,然后将数据在这个地图控件上显示,但是这是VC,不是这么简单,当然地图控件肯定是ESRI提供,微软不会提供一个容纳地图的控件吧?两种方法:插入控件和直接生成相关的类,我们先用第二个方法,顺便回一下MFC。

    1. 添加和地图控件相关的MFC类

    在工程上右键,添加类,选择第一个,如下图:

    弹出一个向导,在可用的ActiveX控件中找到Esri的地图控件,设置生成的类,如下图:

    在CMapMFCView类中添加头文件,定义一个CMapControl2的变量,如下图:

    在类视图中,右键该类,转到"对话框",出现如下界面:

    在上图中拖一个按钮上去,改一下属性,并且添加单击事件(使用类向导完成),然后添加下面的代码(先不要管,我会在后面介绍,CWsClass类是我添加的用来获取要素类的见后面)

    void CMapMFCView::OnClickedBtn()

    {

        

        CRect r;

        GetClientRect(&r);

        CWsClass pWs;

        BSTR bStringWS = SysAllocString(L"D:\\guest\\数据\\1");

        BSTR bStrFcName = SysAllocString(L"1.shp");

        IFeatureClassPtr ipFeatureClassTest=pWs.GetFeatureClass(bStringWS,bStrFcName);

        SysFreeString(bStrFcName);

        SysFreeString(bStringWS);

        IFeatureLayer *ipFeatureLayerX;

        // 查看注册表或者tlh文件{E663A651-8AAD-11D0-BEC7-00805F7C4268} 完完全全通过COM自己创建对象

        HRESULT hr1=::CoCreateInstance(CLSID_FeatureLayer,NULL,CLSCTX_ALL,IID_IFeatureLayer,(void**)&ipFeatureLayerX);

        if(FAILED(hr1))

            return;

        //赋值

        ipFeatureLayerX->putref_FeatureClass(ipFeatureClassTest);

        

        //这句类似Windows的用类来创建窗体(MapControl控件)

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        //获取指针

        IMapControl2Ptr m_ipMapControl=m_MapControl.GetControlUnknown();

        IDataset *ipDsetTest;

        HRESULT hr2=ipFeatureLayerX->QueryInterface(__uuidof(IDataset),(void**)&ipDsetTest);

        if(FAILED(hr2))

            return;

        m_ipMapControl->AddLayer(ipFeatureLayerX,0);

        m_MapControl.put_BackColor(16777215);

        //手动释放

        ipFeatureLayerX->Release();

        ipDsetTest->Release();

        ////ATL方式也是智能指针,我们不需要手动释放

        //CComPtr<IFeatureLayer> ipFtLayer;

        //ipFtLayer.CoCreateInstance(CLSID_FeatureLayer);

        //

        //ipFtLayer->putref_FeatureClass(ipFeatureClassTest);

        //m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        ////QI

        //IDataset *ipDset;

        //ipFtLayer.QueryInterface(&ipDset);

        //BSTR bSName=SysAllocString(L"");

        //ipDset->get_Name(&bSName);

        //ipFtLayer->put_Name(bSName);

        //m_MapControl.put_BackColor(16777215);

        //m_MapControl.AddLayer(ipFtLayer,0);

        //

        ////第三种方式,这个比智能指针有优势,自动QueryInterface

        /*IFeatureLayerPtr ipFeatureLayer;

        HRESULT hr = ipFeatureLayer.CreateInstance(CLSID_FeatureLayer);

        

        ipFeatureLayer->putref_FeatureClass(ipFeatureClassTest);

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        m_MapControl.put_BackColor(16777215);

        m_MapControl.AddLayer(ipFeatureLayer,0);*/

        

        ////第四种方式

        /*IFeatureLayerPtr ipFeatureLayer(CLSID_FeatureLayer);

        ipFeatureLayer->putref_FeatureClass(ipFeatureClassTest);

        IDatasetPtr ipDs(ipFeatureClassTest);

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        m_MapControl.put_BackColor(16777215);

        BSTR bSName=SysAllocString(L"");;

        ipDs->get_Name(&bSName);

        ipFeatureLayer->put_Name(bSName);

        m_MapControl.AddLayer(ipFeatureLayer,0);*/

        

        //m_MapControl.AddShapeFile(L"D:\\guest\\数据\\1",L"1.shp");

    }

    添加一个获取要素类的类,如下图:

    创建完类之后,添加一个获取要素类的函数(该函数的代码,等看完后面的代码说明之后应该就一目了然)

    IFeatureClassPtr CWsClass::GetFeatureClass(BSTR sWorkspacePath,BSTR sFileName)

    {

        CComPtr<IWorkspaceFactory> ipWorkspaceFactory;

        ipWorkspaceFactory.CoCreateInstance(CLSID_ShapefileWorkspaceFactory );

        IWorkspace * m_workspace;

        ipWorkspaceFactory->OpenFromFile(sWorkspacePath,NULL,&m_workspace);

        IFeatureClassPtr ipFeatureClasses;

        if(m_workspace!=NULL)

        {

            IFeatureWorkspacePtr ipFeatWorksapce(m_workspace);

            HRESULT hr=    ipFeatWorksapce->OpenFeatureClass(sFileName,&ipFeatureClasses);

            if(FAILED(hr))

                return NULL;

        }

        return ipFeatureClasses;

    }

    运行后可以看到如下效果(已经说了,不要嘲笑界面)

    1. 代码解释

    需要对Windows编程和MFC有了解,在这里我只介绍和ArcGIS Engine相关的.

    1. 如何实例化对象

      1. 最原始做法(我自己这样称呼,学习COM的时候就是这种方式吧)

    ArcGIS Engine 的组件库都是基于COM技术的,在COM中我们都是通过接口来完成任务的,但是在使用接口之前,必须要知道这个接口指向的是那个对象,也就是说我们必须实例化对象,然后将对象的返回地址赋给一个接口变量,在C#中这种关系似乎很清楚,如下:

    IRasterGeometryProc pRasterGProc = new RasterGeometryProcClass();

    之后我们就可以通过pRasterGProc相关操作了,但是我们现在用的是C++,上面这个是不能用了,如果看过《COM本质论》或者其他和COM相关的书籍的人可能知道在COM中是通过CoCreateInstance函数来获取一个对象的指针变量的,具体的代码如下:

        // 查看注册表或者tlh文件{E663A651-8AAD-11D0-BEC7-00805F7C4268} 完完全全通过COM自己创建对象

        HRESULT hr1=::CoCreateInstance(CLSID_FeatureLayer,NULL,CLSCTX_ALL,IID_IFeatureLayer,(void**)&ipFeatureLayerX);

        if(FAILED(hr1))

            return;

        //赋值

        ipFeatureLayerX->putref_FeatureClass(ipFeatureClassTest);

    在COM中,因为一个类可以实现多个接口,每个接口只可以访问自己定义的方法,如果要使用定义在别的接口中的方法呢?我们自然而然的想到了将这个接口"切换"过去,通俗的讲就是这样,只不过专业术语要做QueryInterface(接口查询/访问,英语的理解就行了,不纠结这个翻译了),在C#中我们使用一个as就可以解决问题,如下(IRaster QI到IRasterProps 上):

    IRaster pRaster = pRasterLayer.Raster;

    IRasterProps pRasterPro = pRaster as IRasterProps;

    C++中的做法和这个不一样,但是目的一样,都是为了"切换"到另外一个接口上,因为要素类实现了IDataset中的方法,那么肯定是可以QI到这个上面去的,下面是做法:

        IDataset *ipDsetTest;

        HRESULT hr2=ipFeatureLayerX->QueryInterface(__uuidof(IDataset),(void**)&ipDsetTest);

        if(FAILED(hr2))

            return;

    我们使用了接口,如果在不用的时候一定要自己给释放掉,如下面所示:

    ipFeatureLayerX->Release();

    ipDsetTest->Release();

    现在要素类有了,要素图层有了,我们将这两个关联起来,一起放到地图控件中去,但是我们的地图控件在哪里,如果习惯了C#中的那种直接将控件拖到窗体上的做法,乍一看一头雾水,别急,待会儿就雨过天晴了。

    Esri在在VC中也提供了我们所谓的控件,我们通常称之为ActiveX控件,在这里我没有插入和拖放,而是直接用类来创建,还记得我刚才插入类的时候,选择了ActiveX控件中的MFC类,又接着选择了MapControl,然后我们的工程中就多了一个类,这个类其实跟一般的MFC类没多大区别,只是这个类里包含和和地图相关的东西,在这里说一下MFC类,MFC类其实就是一个C++类,只是这个类里面封装了Windows的句柄(窗口,菜单等),而ActiveX控件本质上也算是一个窗口吧。我们要得到一个窗口句柄,按照MFC的逻辑:首先实例化一个类,然后用类的Create函数构造这个句柄(MFC在内部已经做了处理):

        //这句类似Windows的用类来创建窗体(MapControl控件)

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        

    //获取指针

        IMapControl2Ptr m_ipMapControl=m_MapControl.GetControlUnknown();

        m_ipMapControl->AddLayer(ipFeatureLayerX,0);

        m_MapControl.put_BackColor(16777215);

        在这里我们可以通过类来操作,也可以通过接口来操作,这里我用m_ipMapControl添加了图层,下面的几个代码中用m_MapControl来操作,请注意这两个的不同。

    如果使用上面的方式,我们要不断的使用CoCreateInstance(),QueryInterface(),Release()等方法,对我们来说有太多的不便,如果忘记了Release可能还会出问题,下来我们讨论我注释掉的几种方法:

    1. ATL方式(智能指针)

    使用智能指针,我们不需要自己手动release等,这个会自动完成,但是对于QI,我们也需要写,但是已经比上面的少了很多代码了:

        CComPtr<IFeatureLayer> ipFtLayer;

        ipFtLayer.CoCreateInstance(CLSID_FeatureLayer);

        ipFtLayer->putref_FeatureClass(ipFeatureClassTest);

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        //QI

        IDataset *ipDset;

        ipFtLayer.QueryInterface(&ipDset);

        BSTR bSName=SysAllocString(L"");

        ipDset->get_Name(&bSName);

        ipFtLayer->put_Name(bSName);

        m_MapControl.put_BackColor(16777215);

        m_MapControl.AddLayer(ipFtLayer,0);

        

    1. Ptr结尾的方式

    当我们import ArcGIS Enine的COM组件是,编译器自己会使用_com_ptr_t帮我们生成,如下图:

    关于中方式和ATL的区别,我在网上查了下,看到下面几点:

    在用VC开发应用程序时,有两个引用计数类可供我们使用。_com_ptr_t与CComPtr,它们都能很好的帮助我们解决引用计数处理。但这两个类还是有一点小小的区别,有的时候这一点区别也是致命的,因此我们必须清楚它们的差别。下面我罗列了它们之间的差别:

    1. CComPtr的&运算符不会释放原指针,而_com_ptr_t会释放原指针。

    2. CComPtr对AddRef与Release做了限制,也就是不充许调用这两个方法,而_com_ptr_t并没有限制。

    3. CComPtr只能接受模版参数指定的指针,_com_ptr_t可以接受任何接口指针,并自动调用QueryInterface得到模板参数指定的指针类型。

    这种方式又可以有两种方式来实例化对象,通过CreateInstance函数和直接用在声明的时候用CLSID:

        IFeatureLayerPtr ipFeatureLayer;

        HRESULT hr = ipFeatureLayer.CreateInstance(CLSID_FeatureLayer);

    和:

    IFeatureLayerPtr ipFeatureLayer(CLSID_FeatureLayer);

    ipFeatureLayer->putref_FeatureClass(ipFeatureClassTest);

    我也不知道这种方式如何命名,但是这种比ATL的那种方式更智能,这体现在QI的时候也简单了很多:

    IFeatureLayerPtr ipFeatureLayer(CLSID_FeatureLayer);

    ipFeatureLayer->putref_FeatureClass(ipFeatureClassTest);

        IDatasetPtr ipDs(ipFeatureClassTest);//这句话就QI过去了

        m_MapControl.Create(L"Test",WS_CHILD|WS_VISIBLE,r,this,10000);

        m_MapControl.put_BackColor(16777215);

        BSTR bSName=SysAllocString(L"");;

        ipDs->get_Name(&bSName);

        ipFeatureLayer->put_Name(bSName);

        m_MapControl.AddLayer(ipFeatureLayer,0);

    1.  
  • 相关阅读:
    selenium的持续问题解决
    为什么使用Nginx
    [转]性能测试场景设计深度解析
    [转]CentOS7修改时区的正确姿势
    [转]利用Fiddler模拟恶劣网络环境
    [转]什么是微服务
    [转] WebSocket 教程
    [转] Python实现简单的Web服务器
    shell修改配置文件参数
    [转] linux shell 字符串操作(长度,查找,替换)详解
  • 原文地址:https://www.cnblogs.com/zuiyirenjian/p/2843664.html
Copyright © 2011-2022 走看看