SuperMap iObjects for C++ 提供了Window 、Linux(x86) 以及 Linux_Arm(银河麒麟系统+龙芯CPU/飞腾CPU)的产品包 ,从而支持跨平台使用。 本文主要介绍如何使用SuperMap iObjects for C++ 组件产品进行二次开发。主要包含以下几个方面的内容:
- 产品包介绍
- 开发工具说明
- 示例程序使用
- VS开发示例
- Qt开发示例
1. 产品包介绍
SuperMap iObjects for C++ 产品包包含下图中的内容:
- bin : 动态库目录,包含产品运行所需的动态库,该目录中提供了32位和64位的动态库,同时还都分别提供release和debug两个版本。如: /bin/bin是32位realease的库目录,而/bin/bind是32位debug的库目录
- lib : 链接库目录,与bin目录一样,也分别提供了32位和64位的release和debug版本的链接库
- include , include/private: 头文件目录,其中根据不同模块分别创建了目录;注意有些模块放在include/private目录中,注意将include/private也添加到项目的头文件目录设置中
- license :本产品的使用许可声明,以及产品中用到的开源库的授权文件
- sample : 示例程序,其中的 extensions4Qt 是基于Qt对SuperMap C++ 组件封装的了QMapControl等基本功能的库工程,gettingStarted 和 planeShow 是依赖于extensions4Qt的Qt应用示例,用于展示地图操作
- help : 提供了产品入门文档和api文档
- support : 提供了一些字体库和许可驱动包
以上最常用到的是 include 、bin 和 lib,它们提供了C++开发的必要内容。
2. 开发工具说明
SuperMap iObjects for C++ 的二次开发主要使用VS或Qt进行开发,适用的开发软件版本如下:
Windows:
1) VS 2012 及以上版本,如 VS 2012, VS 2017
2) Qt 5.3 及以上版本,如 Qt 5.2, Qt 5.6, Qt 5.9, Qt 5.12
3) VS+Qt开发,在VS上安装合适版本的插件即可
Linux:
1) Qt 4.8.6 的编译器,通常需要使用源码编译
2) Qt Creator4.4, 可根据情况安装,配置好Qt 4.8.6的编译器即可
3. 示例程序使用
示例程序提供的是 Qt 工程,因此需要使用Qt。当然也可以在VS中安装插件,在VS中打开Qt工程,只不过需要重新对工程配置头文件,链接库等。此处简单说明一下,如何在Qt中运行示例工程,更详细的工程配置,请看后面的“Qt 开发示例”。以运行gettingStarted 为例。
1. 在 Qt 中依次打开 extensions4Qt 和 gettingStarted
2. 将需要使用的bin目录添加到构建环境PATH中,如下图所示,添加了 bin\bind_x64所在的目录
3. 编译extensions4Qt,运行 gettingStarted即可
4. VS 开发示例
1. 创建一个MFC工程: FirstSuperMap
此处我们创建一个基于对话框的MFC应用,,如下图所示
然后,从“资源视图”打开窗口布局,删除 文本和按钮控件,结果如下图所示:
项目创建好后,接下来进行项目配置,此处以debug x64的为例。
2. 配置运行库
打开项目属性页,找到“配置属性 -> 调试 -> 环境”,将运行库(bin/bind_x64)的路径添加到项目运行环境中,如图:
若不在工程上配置,也可在系统环境变量中配置,注意将其添加到PATH的最前面,并重启VS.
3. 配置头文件目录
在“ C/C++ -> 常规 -> 附加包含目录” 中, 添加头文件目录(include 和 include/private),如图:
4. 增加预编译宏(_UGUNICODE),
在“C/C++ -> 预编译器” 中,增加预编译宏 _UGUNICODE, 如图:
5. 启用宽字符
在“C/C++ -> 语言 -> 将 Wchar 视为内置类型”,设置为“是”,以启用宽字符,如图:
6. x64 版本编译时“字节数超过对象格式限制: 请使用 /bigobj 进行编译”,设置如下:
7. 配置连接库目录
在链接器配置中,添加链接库目录(lib/libd_x64), 如图:
8. 配置所需要的库文件名称
在链接器配置中,添加所需的链接库库文件名称,如下:
SuBased.lib
SuElementd.lib
SuDrawingd.lib
SuEngined.lib
SuFileParserd.lib
SuGeometryd.lib
SuGraphicsd.lib
SuMapd.lib
SuMapEditord.lib
SuOGDCd.lib
SuToolkitd.lib
SuWorkspaced.lib
SuBase3Dd.lib
SuSpatialIndexd.lib
SuChartBased.lib
配置如图:
9. 实现地图窗口
首先,创建一个 MapControl 类,用于实现对地图窗口的基本封装,也方便重复使用,并且使得 MapControl 同时支持 VS MFC 框架和 Qt 框架的窗口。
(1) 在 MapControl.h 文件,如下所示:
#ifndef MAPCONTROL_H
#define MAPCONTROL_H
#include "MapEditor/UGMapEditorWnd.h"
#include "Graphics/UGGraphicsManager.h"
#include "Drawing/UGDrawParamaters.h"
#include "Geometry/UGGeoPoint.h"
using namespace UGC;
// For Callback
#if defined WIN32 || defined _WINDOWS
#define SuCALLBACK __stdcall
#else
#define SuCALLBACK
#endif
class MapControl
{
//Constructor
public:
/*
*@en
*@pInvalidateCallBack A callback function pointer through which to invalidate the Window if map content is updated
*@pWnd the Window which owns the invalidate callback function
*/
MapControl(INVALIDATEPROC pInvalidateCallBack, void* pWnd);
virtual ~MapControl();
...
};
(2) 在 MapControl.cpp 文件,如下所示:
#include "stdafx.h"
#include "MapControl.h"
/************** 地图窗口相关 ****************/
/**************** Invalidate CallBack used in MapControl *********************/
void SuCALLBACK InvalidateCallbackMapControl(void * p)
{
MapControl* pThis = (MapControl*)p;
pThis->Invalidate();
}
MapControl::MapControl(INVALIDATEPROC pInvalidateCallBack, void* pView)
{
m_pUGMapWnd = NULL;
m_pGraphicsImage = NULL;
m_pGraphicsImageOld = NULL;
m_pInvalidateCallback = NULL;
m_IsMinSized = false;
m_pWnd = pView;
m_pInvalidateCallback = pInvalidateCallBack;
//Initialize(pInvalidateCallBack, pView);
Initialize(InvalidateCallbackMapControl, this);
/*UGWorkspace* pWorkspace = m_pUGMapWnd->m_mapWnd.m_Map.GetWorkspace();
if(pWorkspace == NULL){
pWorkspace = new UGWorkspace();
m_pUGMapWnd->m_mapWnd.m_Map.SetWorkspace(pWorkspace);
}*/
m_pInnerWorkspace = new UGWorkspace();
m_pWorkspace = NULL;
SetWorkspace(m_pInnerWorkspace);
mNeedRedraw = false;
mIsInWorkspace = false;
}
...
(3) 在 FirstSuperMapDlg.h 文件中,引入 MapControl.h,并声明一个 MapControl 指针 , 如所示:
// FirstSuperMapDlg.h
private:
MapControl* m_pMapControl;
(4) 在 FirstSuperMapDlg.cpp 文件定义一个全局的地图刷新回调函数,如下所示:
// 地图刷新回调
void SuCALLBACK InvalidateCallBack(void* pWnd)
{
// send message to view for invalidating itself
FirstSuperMapDlg *pView = (FirstSuperMapDlg *)pWnd;
if (pView->m_hWnd != NULL && pView->IsWindowVisible())
pView->Invalidate(false); // not redraw background
}
(4) 在 FirstSuperMapDlg 的构造函数初始化 m_pMapControl,并且先检查了组件许可的检查是否有效,如下所示:
// FirstSuperMapDlg.cpp
FirstSuperMapDlg::FirstSuperMapDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_FIRSTSUPERMAP_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
bool isValid = UGLicense::VerifyLicense(UGLicense_iObjectsCppCore);
if (isValid) {
m_pMapControl = new MapControl(InvalidateCallBack, this);
}
else {
m_pMapControl == NULL;
}
assert(m_pMapControl != NULL);
}
(5) 修改 FirstSuperMapDlg::OnPaint() 函数,从而将地图绘制到窗口上,如图所示:
// FirstSuperMapDlg.cpp
void FirstSuperMapDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientRect(&rect);
m_pMapControl->OnDraw(rect.left, rect.top, rect.right, rect.bottom, dc.m_hDC);
CDialogEx::OnPaint();
}
(6) 在 FirstSuperMapDlg.h 的 FirstSuperMapDlg 类声明需要的鼠标事件函数,如下所示:
// FirstSuperMapDlg.h
public:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
afx_msg void OnSize(UINT nType, int cx, int cy);
(7) 在 FirstSuperMapDlg.cpp 的 BEGIN_MESSAGE_MAP 增加上述事件命令,并实现对应函数,如下所示:
BEGIN_MESSAGE_MAP(FirstSuperMapDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_MOUSEMOVE()
ON_WM_MOUSEWHEEL()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_RBUTTONDBLCLK()
ON_WM_SIZE()
END_MESSAGE_MAP()
// FirstSuperMapDlg.cpp
void FirstSuperMapDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
//CPaintDC dc(this);
m_pMapControl->OnMouseMove(nFlags, point.x, point.y, ::GetDC(this->m_hWnd));
CDialogEx::OnMouseMove(nFlags, point);
}
...
10. 增加打开工作空间和地图的代码
在地图窗口初始化成功后,打开工作空间,并打开地图, 如下:
// FirstSuperMapDlg.cpp
FirstSuperMapDlg::FirstSuperMapDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_FIRSTSUPERMAP_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
bool isValid = UGLicense::VerifyLicense(UGLicense_iObjectsCppCore);
if (isValid) {
m_pMapControl = new MapControl(InvalidateCallBack, this);
// Open workspace file
UGString wkPath = _U("E:\\SuperMap\\iObject_CPP\\1000\\sample\\data\\China400_E-map.smwu");
UGWorkspaceConnection wkCon;
wkCon.m_strServer = wkPath;
wkCon.m_nWorkspaceType = UGWorkspace::WS_Version_SMWU;
UGWorkspace* pWorkspace = m_pMapControl->GetWorkspace();
if (pWorkspace->Open(wkCon))
{
if (pWorkspace->m_MapStorages.GetCount() > 0)
{
UGString mapName = pWorkspace->m_MapStorages.GetNameAt(0);
UGbool isOpen = m_pMapControl->GetMapEditWnd()->m_mapWnd.m_Map.Open(mapName);
m_pMapControl->Refresh();
}
}
}
else {
m_pMapControl == NULL;
}
assert(m_pMapControl != NULL);
}
11. 运行该工程, 如下图所示:
5. Qt 开发示例
1. 创建Qt项目: FirstSuperMap
2. 项目设置中,增加环境变量,如 OBJECTSDIR , 用于指定SuperMap iObjects for C++ 所在目录,方便以后修改;如图:
3. 将运行库(bin/bind_x64)的目录添加到PATH中,如图:
4. 在.pro文件中,配置 预编译宏、宽字符、头文件和链接库等
Windows 和 Linux 上配置略有不同,在此提供两种系统的配置,以便迁移到Linux系统, 增加的配置如下:
DEFINES += _UGUNICODE
# Include Path
OBJECTSDIR = "$$(OBJECTSDIR)"
#message($${OBJECTSDIR})
INCLUDEPATH +=$${OBJECTSDIR}/include\
$${OBJECTSDIR}/include/private\
../../../Include
# x64 /bigobj
QMAKE_CXXFLAGS += /bigobj
win32{
# wchar_t
QMAKE_CXXFLAGS += -Zc:wchar_t
# x64
CONFIG(debug, debug|release){
LIBPATH = $${OBJECTSDIR}/lib/libd_x64
LIBS += -lSuBased \
-lSuElementd \
-lSuDrawingd \
-lSuEngined \
-lSuFileParserd \
-lSuGeometryd \
-lSuGraphicsd \
-lSuMapd \
-lSuMapEditord \
-lSuOGDCd \
-lSuToolkitd \
-lSuWorkspaced \
-lSuBase3Dd \
-lSuSpatialIndexd \
-lSuChartBased \
}else:CONFIG(release, debug|release){
LIBPATH = $${OBJECTSDIR}/lib/lib_x64
LIBS += -lSuBase \
-lSuElement \
-lSuDrawing \
-lSuEngine \
-lSuFileParser \
-lSuGeometry \
-lSuGraphics \
-lSuMap \
-lSuMapEditor \
-lSuOGDC \
-lSuToolkit \
-lSuWorkspace \
-lSuBase3D \
-lSuSpatialIndex \
-lSuChartBase \
}
}
unix:{
# 16bit wchar
QMAKE_CXXFLAGS =-fshort-wchar
# 支持C++11
QMAKE_CXXFLAGS += -std=c++11
# 对std库使用旧ABI, 也可在头文件开始处使用#define _GLIBCXX_USE_CXX11_ABI 0, 如此只对该文件起作用;但独立的库更好管理;
# 新ABI在库中的链接名称如_cxx11::std::string, 旧ABI在库中名称如std::string
# GCC 默认值为1,使用新ABI;
DEFINES += _GLIBCXX_USE_CXX11_ABI=0
LIBS += -L$${OBJECTSDIR}/bin/bin \
-lSuBase \
-lSuElement \
-lSuDrawing \
-lSuEngine \
-lSuFileParser \
-lSuGeometry \
-lSuGraphics \
-lSuMap \
-lSuMapEditor \
-lSuOGDC \
-lSuToolkit \
-lSuWorkspace \
-lSuBase3D \
-lSuSpatialIndex \
-lSuChartBase \
}
5. 实现地图窗口
Qt中地图窗口的实现与VS中的类似,MapControl是一个完整的类,因此将其拷贝过来即可。注意,删除 MapContro.cpp 的第一行(#include "stdafx.h"),该行在Qt中不需要。
(1) 在 .pro 文件增加 MapControl的源文件和头文件,以便Qt项目可使用,如下所示:
SOURCES += \
main.cpp \
MainWindow.cpp \
MapControl.cpp
HEADERS += \
MainWindow.h \
MapControl.h
(2) 在 MainWindow.h 文件添加 MapControl 的头文件(#include "MapControl.h"), 定义MapControl指针,如下所示:
private:
MapControl* m_pMapControl;
(3) 在 MainWindow.cpp 文件定义一个全局的地图刷新回调函数,如下所示:
/**************** Map Refresh CallBack *********************/
void SuCALLBACK InvalidateCallback(void * pWnd)
{
MainWindow* pThis = (MainWindow*)pWnd;
if(pThis != NULL)
pThis->update();
}
(4) 在 MainWindow 的构造函数中,初始化 m_pMapControl,并且先检查了组件许可的检查是否有效,如下所示:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
bool isValid = UGLicense::VerifyLicense(UGLicense_iObjectsCppCore);;
if(isValid)
{
m_pMapControl = new MapControl(InvalidateCallback, this);
}else
{
m_pMapControl = NULL;
qDebug("许可无效,请配置SuperMap组件许可.");
}
assert(m_pMapControl != NULL);
}
(5) 在 MainWindow.h 声明绘制函数和鼠标事件函数,如下所示:
private:
unsigned int getMouseOrKeyFlag(QMouseEvent* event);
protected:
virtual void paintEvent(QPaintEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
(6) 在 MainWindow.cpp 中实现上述函数,如下所示:
/*********** Override event functions ************/
void MainWindow::paintEvent(QPaintEvent* event)
{
if(m_pMapControl == NULL){
return;
}
m_pMapControl->OnDraw(0, 0, this->width(), this->height());
uchar* imgData = m_pMapControl->GetImageBytes();
if (imgData != NULL){
QImage* pQImage = new QImage(imgData, this->width(), this->height(), QImage::Format_ARGB32);
QColor background(255, 255, 255);
QPainter painter;
painter.begin(this);
painter.fillRect(0, 0, this->width(), this->height(), background);
painter.drawImage(QRectF(0, 0, this->width(), this->height()), *pQImage);
// paint signal / slot
painter.end();
delete pQImage;
}
else {
// no map data
}
}
unsigned int MainWindow::getMouseOrKeyFlag(QMouseEvent* event)
{
unsigned int flag = 0;
if (event->modifiers() & Qt::ShiftModifier){
flag = flag | UG_MK_SHIFT;
}
if ((event->modifiers() & Qt::ControlModifier)){
flag = flag | UG_MK_CONTROL;
}
return flag;
}
void MainWindow::wheelEvent(QWheelEvent* event)
{
if(m_pMapControl != NULL){
m_pMapControl->OnMouseWheel(0, event->delta(), event->x(), event->y());
}
}
...
(7) 添加打开工作空间和地图的代码,如下所示:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
bool isValid = UGLicense::VerifyLicense(UGLicense_iObjectsCppCore);
if(isValid)
{
m_pMapControl = new MapControl(InvalidateCallback, this);
// Open workspace file
UGString wkPath = _U("E:\\SuperMap\\iObject_CPP\\1000\\sample\\data\\China400_E-map.smwu");
UGWorkspaceConnection wkCon;
wkCon.m_strServer = wkPath;
wkCon.m_nWorkspaceType = UGWorkspace::WS_Version_SMWU;
UGWorkspace* pWorkspace = m_pMapControl->GetWorkspace();
if (pWorkspace->Open(wkCon))
{
if (pWorkspace->m_MapStorages.GetCount() > 0)
{
UGString mapName = pWorkspace->m_MapStorages.GetNameAt(0);
UGbool isOpen = m_pMapControl->GetMapEditWnd()->m_mapWnd.m_Map.Open(mapName);
m_pMapControl->Refresh();
}
}
}else
{
m_pMapControl = NULL;
qDebug("许可无效,请配置SuperMap组件许可.");
}
assert(m_pMapControl != NULL);
}
6. 运行该工程, 如下图所示:
9. 在VS中使用Qt开发
在VS中使用Qt开发,需要指定Qt模块 Core, GUI, Widgets, 其他工程配置和前面的MFC项目配置方式一样;若是打开已有的Qt项目,还需配置Qt的版本和需要的模块。右键项目,选择 Qt -> “Qt 设置选项”打开如下也页面进行配置。
至此,关于 SuperMap iObjects for C++ 的入门开发就介绍完了。示例数据是产品包中带有的,此处就不单独提供了,当然,也可以使用自己的数。示例程序请通过页面上方的资源下载。
更多功能,请参考GitHub项目https://github.com/Jun0x01/JunSuCpp