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 :  提供了一些字体库和许可驱动包

以上最常用到的是 includebin 和  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
#endifclass  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.hprivate:MapControl* m_pMapControl;

(4) 在 FirstSuperMapDlg.cpp 文件定义一个全局的地图刷新回调函数,如下所示:

// 地图刷新回调
void SuCALLBACK InvalidateCallBack(void* pWnd)
{// send message to view  for invalidating itselfFirstSuperMapDlg *pView = (FirstSuperMapDlg *)pWnd;if (pView->m_hWnd != NULL && pView->IsWindowVisible())pView->Invalidate(false); // not redraw background
}

(4) 在 FirstSuperMapDlg 的构造函数初始化 m_pMapControl,并且先检查了组件许可的检查是否有效,如下所示:

// FirstSuperMapDlg.cppFirstSuperMapDlg::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.cppvoid FirstSuperMapDlg::OnPaint()
{CPaintDC dc(this); // device context for paintingCRect rect;GetClientRect(&rect);m_pMapControl->OnDraw(rect.left, rect.top, rect.right, rect.bottom, dc.m_hDC);CDialogEx::OnPaint();
}

(6) 在 FirstSuperMapDlg.h 的 FirstSuperMapDlg 类声明需要的鼠标事件函数,如下所示:

// FirstSuperMapDlg.hpublic: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.cppvoid 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.cppFirstSuperMapDlg::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 fileUGString 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 += /bigobjwin32{# wchar_tQMAKE_CXXFLAGS += -Zc:wchar_t# x64CONFIG(debug, debug|release){LIBPATH = $${OBJECTSDIR}/lib/libd_x64LIBS +=  -lSuBased            \-lSuElementd         \-lSuDrawingd         \-lSuEngined          \-lSuFileParserd      \-lSuGeometryd        \-lSuGraphicsd        \-lSuMapd             \-lSuMapEditord       \-lSuOGDCd            \-lSuToolkitd         \-lSuWorkspaced       \-lSuBase3Dd          \-lSuSpatialIndexd    \-lSuChartBased       \}else:CONFIG(release, debug|release){LIBPATH = $${OBJECTSDIR}/lib/lib_x64LIBS +=  -lSuBase            \-lSuElement         \-lSuDrawing         \-lSuEngine          \-lSuFileParser      \-lSuGeometry        \-lSuGraphics        \-lSuMap             \-lSuMapEditor       \-lSuOGDC            \-lSuToolkit         \-lSuWorkspace       \-lSuBase3D          \-lSuSpatialIndex    \-lSuChartBase       \}
}
unix:{# 16bit wcharQMAKE_CXXFLAGS =-fshort-wchar# 支持C++11QMAKE_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=0LIBS += -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.cppHEADERS += \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 / slotpainter.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 fileUGString 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

SuperMap iObjects for C++ 入门详解(VS + Qt)相关推荐

  1. linux 日志按大小切割_nginx入门详解(六)- 日志切割

    上一章讲解了nginx的目录加密功能,本章重点介绍nginx的日志切割. 笨办法学linux:nginx入门详解(五)- 目录加密​zhuanlan.zhihu.com 在第二章,我们探讨了nginx ...

  2. python怎么安装myqr模块-python二维码操作:对QRCode和MyQR入门详解

    python是所有编程语言中模块最丰富的 生活中常见的二维码功能在使用python第三方库来生成十分容易 三个大矩形是定位图案,用于标记二维码的大小.这三个定位图案有白边,通过这三个矩形就可以标识一个 ...

  3. python语言编程基础-Python语言入门详解!快速学成Python!

    原标题:Python语言入门详解!快速学成Python! 很多技能是被职场所需要的,但很可惜... 这些技能在大学中并学习不到. 大学和职场现实存在的横沟对大部分同学来说难以跨越或碰得头破血流... ...

  4. python语言入门m-Python语言入门详解!快速学成Python!

    今日主题 "Python语言入门详解" 近两年来,Python语言借着数据科学和人工智能的"东风"成为了最流行的编程语言--街头巷尾人们口口相传.同时,Pyth ...

  5. python语言入门详解-python初级教程:入门详解

    python初级教程:入门详解 Crifan Li 目录 前言 .................................................................... ...

  6. 【GCN】图卷积网络(GCN)入门详解

    机器学习算法与自然语言处理出品 @公众号原创专栏作者 Don.hub 单位 | 京东算法工程师 学校 | 帝国理工大学 图卷积网络(GCN)入门详解 什么是GCN GCN 概述 模型定义 数学推导 G ...

  7. python符号格式化设置区间_Python 数值区间处理_对interval 库的快速入门详解

    使用 Python 进行数据处理的时候,常常会遇到判断一个数是否在一个区间内的操作.我们可以使用 if else 进行判断,但是,既然使用了 Python,那我们当然是想找一下有没有现成的轮子可以用. ...

  8. 【JSON】JSON入门详解(二)

    文章目录 JSON基础文章荐读 JavaScript创建JSON对象 JSON与XML的那些事 JSON与XML的相同之处 JSON与XML的不同之处 AJAX相关JSON与XML JSON与XML的 ...

  9. python怎么安装myqr_python二维码操作:对QRCode和MyQR入门详解

    python是所有编程语言中模块最丰富的 生活中常见的二维码功能在使用python第三方库来生成十分容易 三个大矩形是定位图案,用于标记二维码的大小.这三个定位图案有白边,通过这三个矩形就可以标识一个 ...

最新文章

  1. LabVIEW图像灰度测量(基础篇—7)
  2. Python双版本下创建一个Scrapy(西瓜皮)项目
  3. UI设计实用素材|数据可视化UX套件
  4. Windows Terminal 0.9 发布
  5. MS SQL入门基础:查看与修改索引
  6. 如何在SQL Server中发现和处理孤立的数据库用户
  7. 看懂友盟指数,洞察移动行业大趋势
  8. 面试准备——(二)专业知识(1)Linux
  9. 淘宝上卖云控系统靠谱吗?
  10. C++ 使用 OpenCV 实现证件照蓝底换成白底功能(或其他颜色如红色)详解
  11. pitfall override private method
  12. Unity大型场景程序化生成及优化技术—FPS迷宫生成和优化
  13. 【运维】第二节 Prometheus 简介
  14. 四级恋练有词课程 (10课时-朱伟)导学
  15. 李德毅 | 新一代人工智能如何从传统人工智能中脱颖而出
  16. byref和byval什么时候该使用什么时候不该使用
  17. cmap参数 plt_[转]matplotlib - plt.rcParams、matshow/cmap/坐标轴设置
  18. 美团/饿了么外卖CPS联盟返利公众号小程序核心源码
  19. 2021-12-17 每日一练 100元怎么买100个蛋,鸡蛋1毛一个,鸭蛋3元一个,鹅蛋6元一个
  20. windows计划任务执行powershell脚本

热门文章

  1. C语言:rand取随机数用法
  2. 工作多年月薪不过万,30岁的我是否该转行
  3. 掌握计算机基础之Http和Https
  4. 机器学习_06:SVM支持向量机
  5. 另一个.lua文件中的全局变量可以被其它.lua文件读取
  6. 电脑桌面快捷方式左下角有个蓝色圆圈,里面一个问号是怎么回事?可正常打开...
  7. Ubuntu 12.04 设置外接显示器
  8. Win11利用本地组策略编辑器解除网速限制
  9. Android 圆形头像/有外边框的圆形头像CircleImageView自定义控件使用详解
  10. 优酷APP下载视频不清晰怎么解决