你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / Linux开发
基于VC多视图的工程数据可视化
 

高守传  刘小静

    摘 要 通过具体的工程实例,阐述了使用VC6.0实现构造雷达数据的可视化显示及处理平台,并通过视图分割的方法实现了位置标尺。
    关键词  VC, 窗口分割,位图显示,标尺
一、引言
    在遥感、雷达以及超声波探测等工程领域内,经常需要对系统采集的数据进行可视化的直观显示和处理。Visual C++本身就是一个图形的开发界面,它提供了丰富的关于位图操作的函数,对图像处理系统的开发提供了极大的方便。另外,C++数据处理效率高,因此它是工程数据及其处理可视化的主要开发工具。
    本文以探地雷达探测高速路数据的可视化为例,通过设备无关的位图(DIB)实现数据的图像显示,并通过VC窗口分割实现图像标尺。
二、基础知识
    实例实现的工程数据的可视化主要用到了两方面的VC知识:构造位图(BMP文件)实现工程数据的图像显示;通过窗口分割为图像添加标尺以及数据的辅助显示。
    1.BMP文件的结构及显示
    BMP文件中的数据块表示图像的相应的像素值,需要注意的是:图像的像素值在文件中的存放顺序为从左到右,从下到上。也就是说,在BMP文件中首先存放的是图像的最后一行像素,最后才存储图像的第一行像素,但对与同一行的像素,则是按照先左边后右边的的顺序存储的;另外一个需要关注的细节是:文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。位图文件的结构如表1所示。
                                 表1 位图文件结构

BITMAPFILEHEADER

BITMAPINFOHEADER

RGBQUAD

IMAGEDATA


    第一部分为位图文件头BITMAPFILEHEADER,是一个结构,其定义如下:
typedef struct tagBITMAPFILEHEADER {
WORD        bfType;       //位图文件的类型,必须为
DWORD      bfSize;       //位图文件的大小,字节单位
WORD       bfReserved1;     //位图文件保留字,必须为0
WORD       bfReserved2;      //位图文件保留字,必须为0
DWORD     bfOffBits;       //位图文件头到数据的偏移
} BITMAPFILEHEADER;
    第二部分为位图信息头BITMAPINFOHEADER,也是一个结构,其定义如下:
typedef struct tagBITMAPINFOHEADER{
DWORD   biSize;       //该结构所占字节数
LONG            biWidth;       //位图的宽度,像素单位
LONG            biHeight;      //位图的高度,像素单位
WORD           biPlanes;       //目标设备的位面数,必须为1
WORD           biBitCount       //颜色深度,即每个像素所占的位数
DWORD   biCompression;     //位图的压缩类型
DWORD   biSizeImage;     //位图的大小,字节单位
LONG            biXPelsPerMeter;      //位图水平分辨率
LONG            biYPelsPerMeter;       //位图的垂直分辨率
DWORD   biClrUsed;       //位图实际使用的颜色表中颜色数
DWORD   biClrImportant;           //位图显示中比较重要的颜色数
} BITMAPINFOHEADER;
    第三部分为颜色表。当然,这里是对那些需要调色板的位图文件而言的。有些位图,如真彩色图,前面已经讲过,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。颜色表实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2biBitCount个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:
typedef struct tagRGBQUAD {
BYTE    rgbBlue;         //该颜色的蓝色分量
BYTE    rgbGreen;         //该颜色的绿色分量
BYTE    rgbRed;        //该颜色的红色分量
BYTE    rgbReserved;        //保留值
} RGBQUAD;
位图信息头和颜色表组成位图信息结构BITMAPINFO,其定义如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;     //位图信息头
RGBQUAD bmiColors[1];       //颜色表
} BITMAPINFO;
    第四部分就是实际的图像数据了。对于用到颜色表的位图,图像数据就是该像素在颜色表中的索引值。对于真彩色图,图像数据就是实际的R、G、B值。
位图数据记录了位图的每一个像素值或该对应像素的颜色表的索引值。位图的一个像素值所占的字节数:
 当biBitCount=1时,8个像素占1个字节;
 当biBitCount=4时,2个像素占1个字节;
 当biBitCount=8时,1个像素占1个字节;
 当biBitCount=24时,1个像素占3个字节,此时图像为真彩色图像。
    当图像不是为真彩色时,图像文件中包含颜色表,位图的数据表示对应像素点在颜色表中相应的索引值,当为真彩色时,每一个像素用三个字节表示图像相应像素点彩色值,每个字节分别对应R、G、B分量的值,此时图像文件中没有颜色表。
    Windows规定图像文件中一个扫描行所占的字节数必须是4的倍数(即以字为单位),不足的以0填充。
RGBQUAD数据结构中,增加了一个保留字段rgbReserved,它不代表任何颜色,必须取固定的值为“0”。
 DIB位图显示的常用函数为StretchDIBits,其原型如下:
int StretchDIBits(
  HDC hdc,                     //设备环境句柄
  int XDest,                   //目标区域的左上角X坐标
  int YDest,                   //目标区域的左上角Y坐标
  int nDestWidth,              //目标矩形宽度
  int nDestHeight,             //目标矩形高度
  int XSrc,                    //源矩形的左上角X坐标
  int YSrc,                    //源矩形的左上角Y坐标
  int nSrcWidth,               //源矩形宽度
  int nSrcHeight,              //源矩形高度
  CONST VOID *lpBits,               // DIB图像的指针
  CONST BITMAPINFO *lpBitsInfo,    //位图信息结构的地址
  UINT iUsage,                      //使用标志
  DWORD dwRop                    //光栅操作码
);
    2.窗口分割实现多视
    CSplitterWnd类为窗口分割提供了封装,窗口被分成各个窗格后,由该类的对象负责管理。对Windows来说,CSplitterWnd是一个真正的窗口,它完全占据了框架窗口的用户区域,而视窗则占据了分割窗口的窗片区域。CSplitterWnd窗口不参与命令传递机制。CSplitterWnd类中常用函数及实现的功能如表2所示。
            表2  CSplitterWnd类中常用函数及实现的功能    

成员函数

函数说明

Create

创建动态分割窗口

CreateStatic

创建静态分割窗口

CreateView

创建窗格视图

GetColumnCount

获取窗口分割的窗格的列数

GetRowCount

获取窗口分割的窗格的行数

SetRowInfo

为窗格行设置最小宽和理想宽。行的最小值决定了行何时因为太小而不能被显示

SetColumnInfo

为窗格列设置最小高和理想高。列的最小值决定了列何时因为太小而不能被显示

OnDrawSplitter

此函数由框架负责调用,主要用来绘制或者指定分割窗口的确切特征

OnInvertTracker

使用设置分隔条的属性。该函数在调整窗格大小期间由框架负责调用

    使用时,CSplitterWnd对象通常为其父框架窗口CFrameWnd或CMDIChildWnd(MDI应用中)对象的内嵌成员,CSplitterWnd对象的创建过程如下:在父框架窗口中嵌入CSplitterWnd对象成员;重载父框架窗口的CFrameWnd::OnCreateClient成员函数;从上一步重载的函数内部调用Create创建动态分割窗口或者调用CreateStatic创建静态分割窗口。
三、窗口分割及标尺的实现
    图像显示窗口通过视图分割技术提供了水平和垂直标尺,当鼠标在客户窗口移动时,鼠标所在的位置信息(像素)会在标尺上标识。同时,在状态栏中也有两个窗格以数字的形式显示鼠标的位置信息。分割的各窗格及其对应的视图类如图1所示。
 



图1 窗口的分割及对应的视图类
    窗口的分割过程通过自定义一个派生自CSplitterWnd类的CRullerSplliterWnd类来实现。该类的核心为3个成员函数:
 CreateRulers():实现窗口的分割。
 ShowRulers():设置标尺窗口的特性,显示标尺。
 UpdateRulersInfo():实现标尺视图的更新。
   各函数实现的核心代码如下:
BOOL CRullerSplliterWnd::CreateRulers(CFrameWnd *pParent, CCreateContext *pContext)
{
  if (!CreateStatic(pParent, 2, 2))        //拆分窗口  
  return FALSE;              
 //为各个窗格指定对应视图  
 if (!CreateView(0, 0, RUNTIME_CLASS(CCornerView), CSize(0,0), pContext))  
  return FALSE;            
 if (!CreateView(0, 1, RUNTIME_CLASS(CRulerView), CSize(0,0), pContext))    
  return FALSE;              
 if (!CreateView(1, 0, RUNTIME_CLASS(CRulerView), CSize(0,0), pContext))    
  return FALSE;              
 if (!CreateView(1, 1, pContext->m_pNewViewClass, CSize(0,0), pContext))    
  return FALSE;           
 SetColumnInfo(0, 0, 0);    
 SetRowInfo(0, 0, 0);              
 ((CRulerView*)GetPane(0, 1))->SetRulerType(RT_HORIZONTAL);   //指定水平标尺 
  ((CRulerView*)GetPane(1, 0))->SetRulerType(RT_VERTICAL);    //指定垂直标尺
    SetActivePane(1, 1);        //将图像窗格设置为活动视图  
    return TRUE;
}

void CRullerSplliterWnd::ShowRulers()
{
    int nSize = 16;         //标尺窗格的尺寸
    int nSizeBorder = 3;        //分割条尺寸
    SetRowInfo(0, nSize, 0);       //水平标尺窗格高度
    SetColumnInfo(0, nSize, 0);       //垂直标尺窗格宽度
    m_cxSplitterGap  = nSizeBorder;      //分割条尺寸
 m_cySplitterGap  = nSizeBorder;
    RecalcLayout();         //重新排列窗格
}

void CRullerSplliterWnd::UpdateRulersInfo(stRULER_INFO stRulerInfo)
{

 ((CRulerView*)GetPane(0, 1))->UpdateRulersInfo(stRulerInfo); //更新水平标尺
 ((CRulerView*)GetPane(1, 0))->UpdateRulersInfo(stRulerInfo); //更新垂直标尺
}
其中stRULER_INFO结构体为定义的全局结构体,用于存放标尺相关的信息,其定义如下:
struct stRULER_INFO {
    UINT   uMessage;         //消息
    CPoint ScrollPos;          //滚动条的位置
    CSize  DocSize;          //图像的尺寸
 CPoint Pos;          //当前鼠标位置
};
    视图类CRulerView,在此实现标尺的绘制与显示。CRulerView同样派生自CView类,其核心为以下4个成员函数。
 OnDraw():实现标尺的绘制。
 DrawCursorPos():实现在标尺上绘制鼠标位置标记。
 DrawTicker():绘制标尺刻度函数。
 UpdateRulersInfo():更新标尺,实现重绘。
    各函数的实现的核心代码如下:
void CRulerView::OnDraw(CDC* pDC)
{
 CDocument* pDoc = GetDocument();
 m_lastPos = 0;
 int oldMapMode=pDC->SetMapMode(MM_TEXT);  //设置映射方式(像素)
 CFont vFont;         //垂直标尺字体
 CFont hFont;         //水平标尺字体
 vFont.CreateFont(10, 0, 900, 900, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY, VARIABLE_PITCH|FF_ROMAN, "Times New Roman");
 hFont.CreateFont(12, 0, 000, 000, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_TT_ALWAYS, PROOF_QUALITY, VARIABLE_PITCH|FF_ROMAN, "Times New Roman"); 
    CFont* pOldFont  = pDC->SelectObject((m_rulerType==RT_HORIZONTAL)?&hFont:&vFont);//载入字体
 int oldTextAlign = pDC->SetTextAlign((m_rulerType==RT_HORIZONTAL)?(TA_RIGHT|TA_TOP):(TA_LEFT|TA_TOP));//设置文字方向
 int oldBkMode    = pDC->SetBkMode(TRANSPARENT); //设置背景模式
 CRect rulerRect;
 GetClientRect(&rulerRect);       //视图客户窗口作为标尺区域
 if (m_rulerType==RT_HORIZONTAL)     //水平标尺
 {
  rulerRect.right = (int)m_DocSize.cx;     //水平区域
    }
 else //(m_rulerType==RT_VERTICAL)
 {
  rulerRect.bottom = (int)m_DocSize.cy;     //垂直区域
 }
 pDC->FillSolidRect(rulerRect, RGB(200, 200, 200));  //填充背景
 DrawTicker(pDC, rulerRect, 100);      //绘制刻度线
    DrawTicker(pDC, rulerRect, 10, 10, FALSE);
    DrawTicker(pDC, rulerRect, 5, 12, FALSE);
 // 恢复DC为原来默认的设置
 pDC->SetMapMode(oldMapMode);
    pDC->SelectObject(pOldFont);
 pDC->SetTextAlign(oldTextAlign);
 pDC->SetBkMode(oldBkMode);
}

void CRulerView::DrawTicker( CDC* pDC,CRect rulerRect, int nFactor, int nBegin, bool bShowPos,bool thick_flag)
{
    int nSize  = (m_rulerType == RT_HORIZONTAL)?(int)(rulerRect.right/nFactor):(int)(rulerRect.bottom/nFactor);  //刻度数目
    int nTick  = (int)(nFactor);          //刻度间距
    for (int i=1; i<nSize; i++)
 {
  char buffer[100];
  CString num_h;
  num_h.Format("%d", i*nFactor);         //刻度标识
  sprintf(buffer, "%d", i*nFactor);
  if (m_rulerType==RT_HORIZONTAL)        //水平标尺
   {
   if(nTick*i-m_scrollPos.x>=0&&nTick*i-m_scrollPos.x<=1024)
    {
     pDC->PatBlt(nTick*i-m_scrollPos.x, rulerRect.top+nBegin, 1, rulerRect.bottom, BLACKNESS);              //绘制刻度线
     if (bShowPos)//刻度标识
     pDC->TextOut(nTick*i-m_scrollPos.x, rulerRect.top, num_h); //显示文字
    }
   }
  else
   {
    if(nTick*i-m_scrollPos.y<=768&&nTick*i-m_scrollPos.y>=0)
    {
     pDC->PatBlt(rulerRect.left+nBegin, nTick*i-m_scrollPos.y, rulerRect.right, 1, BLACKNESS);              //绘制刻度线
    if (bShowPos)          //刻度标识
     pDC->TextOut(rulerRect.left, nTick*i-m_scrollPos.y, CString(buffer)); //显示文字
    }
   }
 }
}

void CRulerView::DrawCursorPos(CPoint NewPos)
{
 if (((m_rulerType == RT_HORIZONTAL) && (NewPos.x > m_DocSize.cx)) ||
  ((m_rulerType == RT_VERTICAL) && ((NewPos.y) > m_DocSize.cy)))
  return;
 CDC* pDC = GetDC();
 //设置映射模式(像素)
 int oldMapMode = pDC->SetMapMode(MM_TEXT);
 CRect clientRect;
 GetClientRect(&clientRect);
 if (m_rulerType==RT_HORIZONTAL)//水平标尺
 {
  //将以前的鼠标位置标识删除
        pDC->PatBlt(m_lastPos.x, clientRect.top, 1, clientRect.bottom, DSTINVERT);
  //在新的位置绘制鼠标位置标识
  m_lastPos.x = NewPos.x;
        pDC->PatBlt(m_lastPos.x, clientRect.top, 1, clientRect.bottom, DSTINVERT);
 }
 else // (m_rulerType==RT_VERTICAL)
 {
  //将以前的鼠标位置标识删除
        pDC->PatBlt(clientRect.left, m_lastPos.y, clientRect.right, 1, DSTINVERT);
  //在新的位置绘制鼠标位置标识
  m_lastPos.y = NewPos.y;
        pDC->PatBlt(clientRect.left, m_lastPos.y, clientRect.right, 1, DSTINVERT);
 }
 pDC->SetMapMode(oldMapMode);//恢复默认的DC设置
 ReleaseDC(pDC);
}

void CRulerView::UpdateRulersInfo(stRULER_INFO stRulerInfo)
{
    m_DocSize     = stRulerInfo.DocSize;
    m_scrollPos   = stRulerInfo.ScrollPos;
  if (stRulerInfo.uMessage == RW_POSITION)
 {
        DrawCursorPos(stRulerInfo.Pos);     //重绘鼠标所在位置刻度
    }
    else if ((m_rulerType == RT_HORIZONTAL)&& (stRulerInfo.uMessage == RW_HSCROLL) ||
             (m_rulerType == RT_VERTICAL) && (stRulerInfo.uMessage == RW_VSCROLL))
 {
  Invalidate();         //重绘刻度视图
    }
}
    窗口分割类和相应的视图创建完毕后,即可在程序中实现窗口的分割。窗口分割需要在客户窗口创建前完成,因此需要在CChildFrame类中,重载OnCreateClient函数,在其中调用CRullerSplliterWnd类的CreateRulers()函数,实现窗口分割。实现代码如下:
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
 // TODO: Add your specialized code here and/or call the base class
 if (!m_Rulers.CreateRulers(this, pContext))      //分割窗口
 {
        TRACE("Error creation of rulers\n");
        return CMDIChildWnd::OnCreateClient(lpcs, pContext);
    }
 return TRUE;
}
    其中,m_Rulers为在头文件“ChildFrame.h”中声明的CRullerSplliterWnd类对象:
public:
 CChildFrame();
 CRullerSplliterWnd m_Rulers;        //类对象
    另外,在CChildFrame类中,还定义了两个函数ShowRulers()和UpdateRulersInfo(),分别调用CRullerSplliterWnd类的ShowRulers()和UpdateRulersInfo()函数,用于标尺的显示和更新。实现代码如下:
void CChildFrame::ShowRulers()
{
    m_Rulers.ShowRulers();
}
void CChildFrame::UpdateRulersInfo(stRULER_INFO stRulerInfo)
{
    m_Rulers.UpdateRulersInfo(stRulerInfo);
}
    至此窗口分割以及标尺视图的创建已经完成。标尺的更新绘制流程可表示为图2所示。其中方框内显示的为调用的函数,而方框外显示的是函数所在的类。
 



    图2 标尺的更新绘制流程
四、雷达数据的可视化
    采集的雷达数据数据文件为后缀“rde”格式的二进制数据。文件的前1024个字节记录着数据的位数、零偏、采样时窗的宽度、每个测点的采样点数等信息。后面则依次记录各采样点的数据。各探测点的数据的动态范围为0~4096。雷达数据的图像显示实际上就是将每个测点的各个采样数据分别作为一个像素,其灰度反映该点数据的大小。图像的纵向代表测点的各个采样数据,而横向则代表不同的采样点。其实现过程简单表述如下:
(1)打开文件,根据头文件读出相应的参数和各测点的数据。
(2)根据零偏参数将测点数据归一化为-1~+1之间的数。
(3)将归一化的测点数据转化为灰度像素值(0~255,-1为0,+1为255)。
(4)按BMP格式重新排列数据(BMP文件中首先存放的是图像的最后一行像素,最后才存储图像的第一行像素)。
(5)构造BITMAPINFO结构,通过StretchDIBits函数显示位图。
    其中步骤(1)、(2)在文档类CImageFlatDoc的OnOpenDocument函数中实现,其实现代码参见例程源码。
    步骤(3)、(4)在视图类CImageFlatView的OnInitialUpdate函数中实现,实现的核心代码如下:
void CImageFlatView::OnInitialUpdate()
{
……
 lpBufPtr_bmp = new unsigned char[pDoc->data_head.rh_nsamp*(10+pDoc->data_head rh_slice)];
 lpBufPtr_bmp_1 = new unsigned char[pDoc->data_head.rh_nsamp*(10+pDoc->data_head.rh_slice)];
 for(int i=0;i<pDoc->data_head.rh_nsamp*pDoc->data_head.rh_slice;i++)
  lpBufPtr_bmp_1[i] = (unsigned char)((pDoc->lpBufPtr_Raw_data[i]+1)*256/2);//转换为灰度值
 //按BMP格式重新排列数据
 int lie_n = ((int)((double)pDoc->data_head.rh_slice/4 + 0.999))*4; 
 //变为4的整数倍
 for(int hang = 0;hang <pDoc->data_head.rh_nsamp;hang++)
  for(int lie = 0;lie <lie_n+1;lie++)
  if(lie>(pDoc->data_head.rh_slice-1))
  lpBufPtr_bmp[hang*lie_n+lie] = 255;
  else
  lpBufPtr_bmp[hang*lie_n+lie] = lpBufPtr_bmp_1[pDoc->data_head.rh_nsamp*lie+pDoc->data_head.rh_nsamp-hang-1];
  delete[] lpBufPtr_bmp_1;
 CScrollView::OnInitialUpdate();
}
    步骤5在CImageFlatView类的OnDraw函数中实现,核心代码如下:
void CImageFlatView::OnDraw(CDC* pDC)
{
 CImageFlatDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
  int j_bmp;
  BITMAPINFO *lpbminf;
  lpbminf = new BITMAPINFO [sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256]; 
  //对信息头进行赋值
  lpbminf->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  lpbminf->bmiHeader.biWidth = pDoc->data_head.rh_slice;
  lpbminf->bmiHeader.biHeight = pDoc->data_head.rh_nsamp;
  lpbminf->bmiHeader.biPlanes = 1;
  lpbminf->bmiHeader.biBitCount = 8;
  lpbminf->bmiHeader.biCompression = BI_RGB;
  lpbminf->bmiHeader.biSizeImage = pDoc->data_head.rh_nsamp*pDoc->data_head.rh_slice;
  lpbminf->bmiHeader.biXPelsPerMeter = 0;
  lpbminf->bmiHeader.biYPelsPerMeter = 0;
  lpbminf->bmiHeader.biClrUsed = 0;
  lpbminf->bmiHeader.biClrImportant = 0;
  //对调色板进行赋值
  for(j_bmp=0; j_bmp<256; j_bmp++)
   {
    lpbminf->bmiColors[j_bmp].rgbBlue = (BYTE)j_bmp;
    lpbminf->bmiColors[j_bmp].rgbGreen = (BYTE)j_bmp;
    lpbminf->bmiColors[j_bmp].rgbRed = (BYTE)j_bmp;
    lpbminf->bmiColors[j_bmp].rgbReserved = 0;
   }
  //得到DIB信息
  int xSrc = 0, ySrc =0;
  Dst.x = 0;
  Dst.y = 0;
  int dxSrc = lpbminf->bmiHeader.biWidth;
  int dySrc = lpbminf->bmiHeader.biHeight;
 dDst.x = dxSrc*4;
  dDst.y = dySrc;
  m_ImageSize = CSize(dDst.x,dDst.y);
     //显示图象
  SetStretchBltMode(pDC->m_hDC,COLORONCOLOR);
 StretchDIBits(pDC->m_hDC,Dst.x,Dst.y,dDst.x,dDst.y,xSrc,ySrc,dxSrc,dySrc,lpBufPtr_bmp,lpbminf,DIB_RGB_COLORS,SRCCOPY);
  //释放内存
  delete lpbminf;
}
五 结语
    本文利用Visual C++6.0实现了探地雷达数据的可视化平台,在此平台基础上,可以对数据进行各种处理和直观显示。使用时,打开后缀为“rde”格式的数据文件时,首先弹出文件数据参数对话框,如图3所示。设定处理的数据后,即得到图1所示的处理界面。
 



    图3 文件数据参数对话框

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089