首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

ArcEngine 鷹眼功能完整實作:主圖與概覽圖雙向同步技術解析

     編輯:關於VC++

前言

百度地圖、Google Maps 右下角的小地圖視窗即為「鷹眼」功能。拖動鷹眼中的矩形框,主地圖即移動至對應位置。本文深度解析基於 VC6 與 ArcEngine 9.2 的鷹眼實作技術。

核心互動邏輯

鷹眼與主地圖雙向同步體現兩大核心機制:

  1. 主地圖變化 → 鷹眼同步:主地圖範圍改變時,鷹眼矩形框移動至對應位置,指示當前視野在全圖中的位置
  2. 鷹眼操作 → 主地圖同步:鷹眼矩形框移動時,主地圖顯示範圍對應移動

關鍵技術點

  • ESRI MapControl:ArcEngine 顯示地圖的核心控制元件
  • OnAfterDraw 事件:繪製完成後觸發,用於同步矩形框位置與大小
  • 滑鼠事件處理:OnMouseDown、OnMouseMove、OnMouseUp 實現矩形框拖曳

核心類別設計:CEagleEyeCtrl

class CEagleEyeCtrl : public CStatic
{
public:
    CEagleEyeCtrl();
    CMapControlDefault m_MapCtrl;           // 鷹眼地圖控制元件
    IFillShapeElementPtr m_ipFillShapeElement; // 矩形框圖元
    IEnvelopePtr m_ipLastEnvelope;          // 最後有效包絡
    IPointPtr m_ipPoint;                    // 滑鼠點位置
    int m_nAction;
    BOOL m_bDrag;                           // 矩形框拖曳標記
    BOOL m_bDragWnd;                        // 整個鷹眼視窗拖曳標記
    BOOL m_bShow;
    CPoint m_point;                         // 滑鼠上次左鍵按下螢幕座標

    // 事件處理函式
    afx_msg void OnMouseDownMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY);
    afx_msg void OnMouseMoveMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY);
    afx_msg void OnMouseUpMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY);
    afx_msg void OnAfterDrawMapcontrol(const VARIANT FAR& Display, long viewDrawPhase);

    DECLARE_EVENTSINK_MAP()
    DECLARE_MESSAGE_MAP()
};

核心事件處理實作

1. 滑鼠按下:判斷點擊位置並初始化拖曳狀態

void CEagleEyeCtrl::OnMouseDownMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY)
{
    IActiveViewPtr ipActiveView = m_MapCtrl.GetActiveView();
    if (ipActiveView == NULL) return;

    IPointPtr ipPoint(CLSID_Point);
    IRgbColorPtr ipRgbColor(CLSID_RgbColor);
    ipPoint->PutCoords(mapX, mapY);
    ipRgbColor->put_RGB((OLE_COLOR)RGB(255, 0, 0));

    IGraphicsContainerPtr ipGraphicsContainer;
    ipActiveView->get_GraphicsContainer(&ipGraphicsContainer);

    // 建立初始矩形框
    IEnvelopePtr ipEnvelope(CLSID_Envelope);
    ipEnvelope->put_XMax(mapX + 0.0005);
    ipEnvelope->put_XMin(mapX - 0.0005);
    ipEnvelope->put_YMax(mapY + 0.0003);
    ipEnvelope->put_YMin(mapY - 0.0003);

    IRectangleElementPtr ipRectangleElement(CLSID_RectangleElement);
    ISimpleFillSymbolPtr ipSimpleFillSymbol(CLSID_SimpleFillSymbol);
    ISimpleLineSymbolPtr ipSimpleLineSymbol(CLSID_SimpleLineSymbol);
    ipSimpleFillSymbol->put_Style(esriSFSHollow);
    ipSimpleLineSymbol->put_Color(ipRgbColor);
    ipSimpleLineSymbol->put_Style(esriSLSSolid);
    ipSimpleLineSymbol->put_Width(1.8);
    ipSimpleFillSymbol->put_Outline(ipSimpleLineSymbol);

    IElementPtr ipElement = ipRectangleElement;
    ipElement->put_Geometry(ipEnvelope);

    // 判斷點擊是否在現有矩形框內
    if (m_ipFillShapeElement != NULL)
    {
        IElementPtr ipTempElement = m_ipFillShapeElement;
        IGeometryPtr ipGeometry = NULL;
        ipTempElement->get_Geometry(&ipGeometry);
        esriGeometryType esriType = esriGeometryNull;
        ipGeometry->get_GeometryType(&esriType);
        if (esriType == esriGeometryPolygon)
        {
            IEnvelopePtr ipEnvelope2 = NULL;
            IPolygonPtr ipPolygon = ipGeometry;
            ipPolygon->get_Envelope(&ipEnvelope2);
            double xMin, xMax, yMin, yMax;
            ipEnvelope2->QueryCoords(&xMin, &yMin, &xMax, &yMax);
            if (mapX >= xMin && mapX <= xMax && mapY >= yMin && mapY <= yMax)
                m_bDrag = TRUE;   // 在框內:進入拖曳模式
            else
                m_bDrag = FALSE;  // 在框外:移動框架至點擊位置
        }
    }
}

2. 滑鼠移動:拖曳矩形框並即時刷新

void CEagleEyeCtrl::OnMouseMoveMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY)
{
    if (!m_bDrag || m_ipFillShapeElement == NULL) return;

    IActiveViewPtr ipActiveView = m_MapCtrl.GetActiveView();
    if (ipActiveView == NULL) return;

    IGraphicsContainerPtr ipGraphicsContainer;
    ipActiveView->get_GraphicsContainer(&ipGraphicsContainer);

    IElementPtr ipElement = m_ipFillShapeElement;
    IGeometryPtr ipGeometry = NULL;
    ipElement->get_Geometry(&ipGeometry);
    IPolygonPtr ipPolygon = ipGeometry;
    IEnvelopePtr ipEnvelope(CLSID_Envelope);
    ipPolygon->get_Envelope(&ipEnvelope);

    IPointPtr ipPoint(CLSID_Point);
    ipPoint->PutCoords(mapX, mapY);
    ipEnvelope->CenterAt(ipPoint);  // 矩形框中心移至滑鼠位置
    m_ipLastEnvelope = ipEnvelope;

    IEnvelopePtr ipEnvelope2 = m_MapCtrl.GetExtent();
    ipActiveView->PartialRefresh(esriViewForeground, NULL, ipEnvelope2);  // 部分刷新
}

3. 滑鼠釋放:確認位置並通知主地圖更新

void CEagleEyeCtrl::OnMouseUpMapcontrol(long button, long shift, long X, long Y, double mapX, double mapY)
{
    m_bDrag = FALSE;

    IActiveViewPtr ipActiveView = m_MapCtrl.GetActiveView();
    if (ipActiveView == NULL || m_ipFillShapeElement == NULL) return;

    IGraphicsContainerPtr ipGraphicsContainer;
    ipActiveView->get_GraphicsContainer(&ipGraphicsContainer);

    IElementPtr ipElement = m_ipFillShapeElement;
    IGeometryPtr ipGeometry = NULL;
    ipElement->get_Geometry(&ipGeometry);
    IPolygonPtr ipPolygon = ipGeometry;
    IEnvelopePtr ipEnvelope;
    ipPolygon->get_Envelope(&ipEnvelope);

    IPointPtr ipPoint(CLSID_Point);
    ipPoint->PutCoords(mapX, mapY);
    ipEnvelope->CenterAt(ipPoint);
    ipElement->put_Geometry(ipEnvelope);
    m_ipFillShapeElement = ipElement;
    ipGraphicsContainer->UpdateElement((IElementPtr)m_ipFillShapeElement);
    ipActiveView->Refresh();

    // 通知父視窗切換視野
    m_ipPoint = ipPoint;
    GetParent()->GetParent()->SendMessage(WMU_GIS_EAGLE_NOTIFY, 0, 0);
}

4. 繪製完成後:拖曳過程中即時重繪矩形框

void CEagleEyeCtrl::OnAfterDrawMapcontrol(const VARIANT FAR& Display, long viewDrawPhase)
{
    if (m_ipLastEnvelope != NULL && m_bDrag && m_ipFillShapeElement != NULL)
    {
        IActiveViewPtr ipActiveView = m_MapCtrl.GetActiveView();
        if (ipActiveView == NULL) return;

        IElementPtr ipElement = m_ipFillShapeElement;
        IGeometryPtr ipGeometry = NULL;
        ipElement->get_Geometry(&ipGeometry);
        IPolygonPtr ipPolygon = ipGeometry;
        IEnvelopePtr ipEnvelope(CLSID_Envelope);
        ipPolygon->get_Envelope(&ipEnvelope);

        // 取得目前滑鼠位置並更新矩形框
        CPoint pt;
        GetCursorPos(&pt);
        m_MapCtrl.ScreenToClient(&pt);
        IPointPtr ipPoint = m_MapCtrl.ToMapPoint(pt.x, pt.y);
        ipEnvelope->CenterAt(ipPoint);

        // 直接在螢幕 DC 上繪製紅色矩形框
        ISimpleFillSymbolPtr ipSimpleFillSymbol(CLSID_SimpleFillSymbol);
        ISimpleLineSymbolPtr ipSimpleLineSymbol(CLSID_SimpleLineSymbol);
        IRgbColorPtr ipRgbColor(CLSID_RgbColor);
        ipRgbColor->put_RGB((OLE_COLOR)RGB(255, 0, 0));
        ipSimpleFillSymbol->put_Style(esriSFSHollow);
        ipSimpleLineSymbol->put_Color(ipRgbColor);
        ipSimpleLineSymbol->put_Style(esriSLSSolid);
        ipSimpleLineSymbol->put_Width(1.8);
        ipSimpleFillSymbol->put_Outline(ipSimpleLineSymbol);

        IScreenDisplayPtr ipScreenDisplay;
        OLE_HANDLE hDC;
        ipActiveView->get_ScreenDisplay(&ipScreenDisplay);
        ipScreenDisplay->get_hDC(&hDC);
        ipScreenDisplay->StartDrawing(hDC, -1);
        ipScreenDisplay->SetSymbol((ISymbolPtr)ipSimpleFillSymbol);
        ipScreenDisplay->DrawRectangle(ipEnvelope);
        ipScreenDisplay->FinishDrawing();
    }
}

主地圖同步邏輯(概念說明)

主地圖的 OnAfterDraw 事件中,根據目前地圖範圍計算鷹眼矩形框大小與位置;接收 WMU_GIS_EAGLE_NOTIFY 訊息時,依鷹眼矩形框中心點設定主地圖 Extent 實現視野跳轉。

技術亮點總結

技術點實作關鍵
雙向同步主圖 OnAfterDraw 更新鷹眼框;鷹眼滑鼠事件驅動主圖 Extent 變更
拖曳流暢度OnMouseMove 中 PartialRefresh 區域重繪;OnAfterDraw 中直接繪製避免閃爍
座標轉換螢幕座標 ↔ 地圖座標:ScreenToClient + ToMapPoint
圖元管理GraphicsContainer 管理圖層元素;FillShapeElement 代表可拖曳矩形框
通訊機制自訂訊息 WMU_GIS_EAGLE_NOTIFY 跨視窗通知

擴充應用建議

  • 多層級鷹眼:支援多張不同比例尺概覽圖切換
  • 矩形框縮放:滑鼠滾輪調整框大小對應主地圖縮放
  • 半透明遮罩:矩形框外區域加深顯示聚焦效果
  • 座標資訊提示:滑鼠懸停顯示經緯度/投影座標
  • 鍵盤輔助操作:方向鍵微調、Shift+拖曳鎖定軸向
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved