通过对超图嵌入式GIS实例程序的研究,我总共调通了4个程序,分别是地图的基本操作、地图的编辑、地图的查询、地图的路径分析功能。下面是我将在开发过程中的学习笔记和常见错误及其解决方法总结如下。
目录
第一节 地图的基本操作
第二节 地图的编辑
第三节 地图的查询
第四节 地图的路径分析
第五节 调试过程中常见错误及其解决方法
第一节地图的基本操作
地图的基本操作在刘老师给的eSuperMap开发基本步骤中已经给出,下面我总结的是我在基本操作中遇到的其他问题和细节如下:
一、菜单栏的使用。一般我们在新建程序的时候系统自动创建了一个菜单,但是这个菜单一般只有一个菜单项,且添加后也不能用。解决方法如下:
1、 首先在资源里添加菜单资源,ID号取名为IDR_MENUTEST,
2、 编辑菜单;
3、 此时的菜单不能显示,下面来解决现实的问题;
⑴在MainFrm.h头文件里声明菜单对象,一般不用声明自带。
⑵在MainFrm.cpp里添加代码:
⑶菜单添加完成。
二、工具栏的使用。
⑴首先添加资源如下图,选择ToolBar选项,新建ToolBar。
⑵打开ToolBar,绘制图标,也可以从别的打开的项目里复制粘贴。
建议自己制作,有创意,并给每个图标ID命名,比如ID_MAP_ZOOMIN。
⑶添加代码,加载toolBar工具栏:
①先在里声明一个工具栏对象
②在MainFrm.cpp中的
int CMainFrame::OnCreate()函数里添加代码:
//创建通用工具条
if(!m_wndToolBar.CreateEx(this)||
!m_wndToolBar.LoadToolBar( IDR_TOOLBAR1 ))
{
TRACE0("Failed to create toolbar\n");
return -1;
}
③在View里给每个图标添加事件响应函数;
④工具栏的使用完成。
三、开机时界面图片的加载。
⑴首先添加资源如下图,选择Bitmap选项,新建Bitmap。
具体在CEditorView类中OnDraw函数里实现。
// CEditorView drawing
void CEditorView::OnDraw(CDC* pDC)
{
CEditorDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rcClient;
GetClientRect( &rcClient );
if(!m_bFileOpened)
{
//显示启动画面,地图还没有显示的时候!
CDC dc;
dc.CreateCompatibleDC(pDC);
CBitmap bmp;
bmp.LoadBitmap( IDB_BITMAP_STARTUP_PAGE );// IDB_BITMAP_STARTUP_PAGE位图ID号
CBitmap *pBitmapSaved = dc.SelectObject(&bmp);
pDC->BitBlt(0,0,rcClient.Width(),rcClient.Height(),&dc,0,0,SRCCOPY);
dc.SelectObject(pBitmapSaved);
dc.DeleteDC();
bmp.DeleteObject();
}
else
{
m_MapWnd.OnDraw(pDC, rcClient,rcClient); //显示地图
}
}
里实现。
第二节地图的编辑操作
地图编辑操作的基本步骤总结如下:
第一步:添加对话框选择可编辑图层:
更改ComboBox属性,(不改的话会出现下拉菜单只能有一个选择项的情况)
右键查看代码,找到如下代码并更改成如下样式保存:
COMBOBOX IDC_COMBOX,37,27,111,100,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
第二步:添加类,变量,和函数:
双击对话框添加类取名:SelectEditLayer
1、添加变量(SelectEditLayer类):
CComboBox EditLayerComboBox; // 可编辑图层对象名,方法是右击ComboBox添加变量
CString mm_strLayerName; // 记录可编辑图层名
CSeMapWnd *m_pMapWnd; //地图窗口指针
2、添加函数(SelectEditLayer类):
void SetLayerName(void); //将数据源中的图层名加到对话框图层列表中
CString GetLayerName(void); //获得ComboBox中数据集对应的的图层名
virtual BOOL OnInitDialog();//虚函数初始化
3、添加代码(SelectEditLayer类):
//菜单栏选择可编辑图层ID消息事件
void CProgramView::OnSelectlayer()
{
//弹出选择可编辑图层对话框
SelectEditLayer dlg;
dlg.m_pMapWnd = &m_MapWnd;
if (dlg.DoModal()== IDOK )
{
CString m_strEditableLayer = dlg.m_strLayerName;
//设置可编辑图层
m_MapWnd.SetEditableLayer( m_strEditableLayer )
m_MapWnd.Refresh();
}
}
//将数据源中的图层名加到对话框图层列表中
void SelectEditLayer::SetLayerName(void)
{
CString strLayerName;
EditLayerComboBox.ResetContent();
if (m_pMapWnd!=NULL)
{
int nLayerCount=m_pMapWnd->GetLayerCount();
for(int i=0;i<m_pMapWnd->GetLayerCount();i++)
{
CSeLayer *pLayer=m_pMapWnd->GetLayerAt(i);
strLayerName=pLayer->m_strLayerName;
//去掉"@+数据源名"
int nPos=strLayerName.Find(_T("@"));
strLayerName=strLayerName.Left(nPos);
EditLayerComboBox.InsertString(i,strLayerName);
}
EditLayerComboBox.SetCurSel(0);
}
}
//获得对话框中的图层名
CString SelectEditLayer::GetLayerName(void)
{
CString strLayerName;
int nSelItem = EditLayerComboBox.GetCurSel();
if (m_pMapWnd!=NULL)
{
CSeLayer *pLayer=m_pMapWnd->GetLayerAt(nSelItem);
strLayerName = pLayer->m_strLayerName;
}
return strLayerName;
}
//确定按钮双击事件
void SelectEditLayer::OnBnClickedOk()
{ // TODO: 在此添加控件通知处理程序代码
m_strLayerName = GetLayerName();
CDialog::OnOK();
}
//取消按钮双击事件
void SelectEditLayer::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
OnCancel();
}
//初始化对话框
BOOL SelectEditLayer::OnInitDialog()
{ //初始化
CDialog::OnInitDialog();
//将图层名添加到下拉列表中
SetLayerName();
return TRUE;
}
此时为止实现了打开时加载数据集名字列表到ComboBox中的功能
现在开始实现编辑图层功能(View类事件函数):
//添加面
void CProgramView::OnAddregion()
{
// TODO: 在此添加命令处理程序代码
//设置或取消用户操作为添加面对象
if( m_MapWnd.GetAction() == CSeDrawParameters::uaEditCreatePolygon )
{
m_MapWnd.SetAction( CSeDrawParameters::uaNull );
}
else
{
m_MapWnd.SetAction( CSeDrawParameters::uaEditCreatePolygon );
}
}
//添加节点
void CProgramView::OnAddpoint()
{
//设置或取消用户操作为添加点对象
if( m_MapWnd.GetAction() == CSeDrawParameters::uaEditCreatePoint )
{
m_MapWnd.SetAction( CSeDrawParameters::uaNull );
}
else
{
m_MapWnd.SetAction( CSeDrawParameters::uaEditCreatePoint );
}
}
//编辑节点
void CProgramView::OnEditpoint()
{
//设置或取消用户操作为编辑节点
if( m_MapWnd.GetAction() == CSeDrawParameters::uaEditVertexEdit )
{
m_MapWnd.SetAction( CSeDrawParameters::uaNull );
}
else
{
m_MapWnd.SetAction( CSeDrawParameters::uaEditVertexEdit );
}
}
完成调试,编辑操作完成。
第三节地图的查询操作
SQL查询总结
SQL查询:根据SQL语句查询
第一步:添加SQL查询对话框,如图:
更改ComboBox属性:
右键查看代码,找到如下代码并更改成如下样式保存:
COMBOBOX IDC_COMBOX1,37,27,111,100,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBOX1,37,27,111,100,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBOX1,37,27,111,100,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
第二步:添加类,变量,和函数:
双击对话框添加类,类名为SQLQuery
1、 添加变量:
CComboBox m_DatasetName; //要查询的数据集的名称
CComboBox m_FieldName; //要查询的数据集的字段名
CComboBox m_Operator; // 运算符
CString strLayerName; //记录查询图层名
CSeMapWnd* m_pMapWnd;
CString m_SQL;//SQL语句
CSeRecordset *m_pQueryRecord;//记录查询出来的结果
2、 添加函数:
void SetLayerNameToCombo(void); //将数据源中的图层名加到对话框图层列表中
CString GetLayerName(void); //获得对话框中的图层名
virtual BOOL OnInitDialog();//初始化
第三步: 打开对话框 ⑴将地图图层名添加到ComboBox中
⑵选择图层将字段信息更新到ComboBox中
⑶将运算符列表添加到ComboBox中
⑷选择字段名将SQl语句更新
⑸选择运算符将SQL语句更新
具体实现:
⑴将地图图层名添加到ComboBox中
void SQLQuery::SetLayerNameToCombo(void)
{
m_DatasetName.ResetContent();
if (m_pMapWnd!=NULL)
{
int nLayerCount=m_pMapWnd->GetLayerCount();
for(int i=0;i<m_pMapWnd->GetLayerCount();i++)
{
CSeLayer *pLayer=m_pMapWnd->GetLayerAt(i);
strLayerName=pLayer->m_strLayerName;
//去掉"@+数据源名"
int nPos=strLayerName.Find(_T("@"));
strLayerName=strLayerName.Left(nPos);
m_DatasetName.InsertString(i,strLayerName);
}
m_DatasetName.SetCurSel(0);
}
}
⑵选择图层将字段信息更新到ComboBox中
选择ComboBox,选择属性里面的事件,找到CBN_SELCHANGE事件,添加事件函数
//选择字段后将字段名更新到SQl中
void SQLQuery::OnCbnSelchangeCombo2()
{
//当选择字段名时,SQL查询语句+字段名
UpdateData( TRUE );
CString strField;
//获取选择的字段名
int nItem = m_FieldName.GetCurSel();
m_FieldName.GetLBText( nItem, strField );
//SQL查询语句+字段名
m_SQL += strField + _T(" ");
UpdateData( FALSE );
}
⑶将运算符列表添加到ComboBox中,操作在初始化函数里实现,初始化全部代码
BOOL SQLQuery::OnInitDialog()
{ //初始化
CDialog::OnInitDialog();
//将图层名添加到下拉列表中
SetLayerNameToCombo();
//填入操作符
CString strOperator[10];
strOperator[0] = _T("=");
strOperator[1] = _T(">");
strOperator[2] = _T(">=");
strOperator[3] = _T("<");
strOperator[4] = _T("<=");
strOperator[5] = _T("and");
strOperator[6] = _T("not");
strOperator[7] = _T("or");
strOperator[8] = _T("like");
strOperator[9] = _T("in");
for ( int j=0; j<10; j++ )
{
m_Operator.InsertString( j, strOperator[j]);
}
m_Operator.SetCurSel(0);
return TRUE;
}
⑷选择字段名将SQl语句更新
//选择字段后将字段名更新到SQl中
void SQLQuery::OnCbnSelchangeCombo2()
{
//当选择字段名时,SQL查询语句+字段名
UpdateData( TRUE );
CString strField;
int nItem = m_FieldName.GetCurSel();//获取选择的字段名
m_FieldName.GetLBText( nItem, strField );
m_SQL += strField + _T(" "); //SQL查询语句+字段名
UpdateData( FALSE );
}
⑸选择运算符将SQL语句更新
//选择运算符后将运算符名更新到SQl中
void SQLQuery::OnCbnSelchangeCombo3()
{
//当选择字段名时,SQL查询语句+字段名
UpdateData( TRUE );
CString strField;
//获取选择的字段名
int nItem = m_Operator.GetCurSel();
m_Operator.GetLBText( nItem, strField );
//SQL查询语句+字段名
m_SQL += strField + _T(" ");
UpdateData( FALSE );
}
第四步:开始查询
双击查询按钮,添加事件函数
void SQLQuery::OnBnClickedOk()//开始查询
{
UpdateData( TRUE );
//获取与图层相对应的数据集
CSeLayer *pLayer = m_pMapWnd->GetLayer(strLayerName );
if ( pLayer )
{
CSeDatasetVector *pDataset = (CSeDatasetVector *)pLayer->GetDataset();
//SQL查询
m_pQueryRecord = pDataset->QueryByGeneral( m_SQL );
//显示结果
if ( m_pQueryRecord ) //如果查询的结果不为空
{
long nCount = m_pQueryRecord->GetCount();
if ( nCount > 0 )//查询到对象...
{
//弹出查询到的记录个数消息框
CString strMsg;
strMsg.Format( _T("共查询出%d个记录,请用工具条按钮查看"), nCount);
::AfxMessageBox( strMsg, MB_OK );
//清空选择集
m_pMapWnd->GetSelection()->ReleaseAll();
m_pMapWnd->GetSelection()->m_pDataset = pDataset;
//将所查询到的对象都加入到选择集中突出显示
if( m_pQueryRecord->MoveFirst() )
{
long nID = 0;
while( !m_pQueryRecord->IsEOF() )
{
nID = m_pQueryRecord->GetID();//获取对象ID
if( nID >= 0 )
{//将对象ID加入到选择集中
m_pMapWnd->GetSelection()->Add( nID );
}
m_pQueryRecord->MoveNext();
}
}
}
else//没有查询到对象,弹出提示消息框...
{
CString strMsg;
strMsg = _T ("没有查到符合条件的记录,请重新查询!");
::AfxMessageBox( strMsg, MB_OK );
}
}
}
OnOK();
}
调试查看结果。
第四节地图的路径分析操作
第一步:在View类里声明一个路径分析类的对象:
public: CSePathAnalyst m_PathAnalyst;//定义一个路径分析类对象
第二步:调用SetDatasetNetwork方法,具体方法如下:
⑴得到网络分析数据集
CSeDatasetVector* pDatasetNetwork = NULL;
CSeDataSource* pDataSource = theApp.m_WorkSpace.GetDataSourceByAlias(_T("新世界阳光花园"));
if( pDataSource != NULL )
{
CSeDatasetVector* pDataset = (CSeDatasetVector *)pDataSource->GetDataset(_T("道路网络"));
pDatasetNetwork= pDataset;
}
⑵设置网络分析数据集
boolm_setNetWork= m_PathAnalyst.SetDatasetNetwork( pDatasetNetwork );
⑶设置分析模型
m_bRuleParamSetted =true;//判断是否设置分析模型成功,成功的话在LbuttunDown里
m_nAction=10;//值为时表明为路径分析模式,在在LbuttunDown里用到
m_MapWnd.SetAction( CSeDrawParameters::uaNull);//此时将地图上的操作设为空操作
⑷在C路径分析View::OnLButtonDown(UINT nFlags, CPoint point)里进行路径分析
if(m_nAction==10)//如果操作为路径分析模式的话
{//路径搜索
if(m_bRuleParamSetted)//已经设置了路径分析参数
{ //设置路径分析模式和模型后
CSeDatasetVector *pSeDatasetNetwork = m_PathAnalyst.GetDatasetNetwork();
if( pSeDatasetNetwork )//如果网络数据集存在的话
{
//获取网络数据集的节点子数据集
CSeDatasetVector *pSeDatasetNode = pSeDatasetNetwork->GetChildDataset();
if( pSeDatasetNode )//如果网络数据集的节点子数据集存在的话
{
CPoint pntNode; //得到的一个最近的点
CPoint pntMouse = CPoint(point);//获取鼠标点对象
//将像素坐标转换为地图坐标
m_MapWnd.GetDrawParam()->ClientToMap(&pntMouse);
//获取鼠标点击点最近的节点
long nNodeID = pSeDatasetNetwork->FindNearestNode( pntMouse, pntNode );//pntMouse点击点,pntNode得到的最近的点
if( nNodeID >= 0 )//如果找到相应节点
{
if (m_nFromNodeID == -1)//路径分析的起始点
{
m_nFromNodeID = nNodeID; //将得到的最近节点ID赋值给起始点ID
m_pntToNode = m_pntFromNode = pntNode;//起始点坐标
m_pntFromNode = pntNode;
//刷新跟踪层,等待将分析的路径放入
if( m_MapWnd.m_TrackingLayer.GetCount() > 0 )
{
m_MapWnd.m_TrackingLayer.ReleaseAll();
m_MapWnd.RefreshTrackingLayer(false);
}
//显示起始点(为实现)
//显示起始点(为实现)
}
else//在执行完上面的If语句后,开始设置路径分析的终止点
{
m_pntToNode = pntNode;//将得到的最近点的点的坐标给终止点坐标
//DisplayNodePoint(m_pntToNode);//显示终止点未实现
CDWordArray arrIDs;//路径的各个弧段ID【输出】
CDWordArray pntIDs;//路径的各个节点ID【输出】
double dPathLength = 0.0;//路径的长度
//开始分析
m_PathAnalyst.Analyse( m_nFromNodeID, nNodeID, arrIDs,dPathLength,pntIDs );
CSeGeometry *pSeGeometry = NULL;
CSeDatasetVector *pSeDtV = NULL;
long nCount = arrIDs.GetSize();//获取分析后路径弧段的个数
if( nCount == 0 )
{//如果没有弧度,即没有找到路径
//AfxMessageBox(_T("Hello World"),MB_OK,NULL);
}
else
{
//AfxMessageBox(_T("Hello World"),MB_OK,NULL);
//如果找到路径
//获取弧度的记录集
CSeRecordset* pSeRecordset = pSeDatasetNetwork->QueryByIDs( (long *)arrIDs.GetData(), nCount );
//设置跟踪图层的风格
CSeStyleLine *pStyle = new CSeStyleLine;
pStyle->m_dwPenStyle = 0;
pStyle->m_nPenWidth = 8;
pStyle->m_nPenColor = RGB(255,0,0);
m_MapWnd.m_TrackingLayer.SetStyle( pStyle );
//将相应的弧段几何对象加入到跟踪层上
if( pSeRecordset )
{
if( pSeRecordset->MoveFirst() )
{
while( !pSeRecordset->IsEOF() )
{
pSeRecordset->GetGeometry(pSeGeometry);
if (pSeGeometry)
{
m_MapWnd.m_TrackingLayer.AddEvent(pSeGeometry,_T(""));
}
pSeRecordset->MoveNext();
}
} pSeDatasetNetwork->ReleaseRecordset(pSeRecordset);
}
//刷新跟踪图层
if( m_MapWnd.m_TrackingLayer.GetCount() > 0 )
{
m_MapWnd.RefreshTrackingLayer();
}
}
m_nFromNodeID = -1;//将起始点ID置-,待第二次分析
}
}
}
}
}
}
第五节调试过程中常见错误及其解决方法
1、MFC智能设备应用程序不能创建的问题:
解决方法:
⑴关闭VS2005,运行 RegEdit.exe
⑵找到以下路径:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Ext\PreApproved
⑶右键新建项:{D245F352-3F45-4516-B1E6-04608DA126CC}
⑷关闭注册表
⑸再运行VS2005即可了
2、.exe不是一个有效的PocketPC应用程序
解决方法:更改命令行成下面
3、RSFC.h找不到的问题
解决方法:库的路径设置不正确,设置附加库目录。
4、模拟器中只有一个.exe文件,无法执行。
解决方法:库目录没设置好,设置好后调试应该能解决。
5、.exe启动不了的问题
解决方法:可能类库引用有问题,往模拟器上上传类库应该可以解决。
6、在静态库中使用MFC引发的问题。
调试错误如图:
解决方法:解决方案管理——属性页——配置属性——常规——项目默认值——MFC的使用属性改为:在共享DLL中使用MFC。
7、调试中出现无法解析的外部符号的另外一个可能原因是,在类中定义了一个函数,而没有实现,却直接调用。
解决方法:找到函数实现之。