zoukankan      html  css  js  c++  java
  • Bridge? 一个GIS二次开发中常用的设计模式

    问题由来

    GIS二次开发中经常需要写很多简单操作的重复代码,小到一般的一般的放大缩小,大到类似MapX的自定义工具(UserTools),或者SuperMap Object中的一些分析功能,这些功能的实现散布在程序的不同过程和事件,不仅每次书写麻烦,而且不易维护。在《应用Visual Basic的事件机制设计可复用的大粒度GIS组件》一文中,笔者提出了使用委托模式(非.net的委托)和事件机制,将这些功能设计为一个独立的自定义控件(User Control)或者类模块的思路,本文将以MapX的自定义工具为例来说明这个问题。

    一般的解决方法

    MapX中可以通过自定义工具来完成一些控件本身没有提供的功能,例如距离的量测。要自定义一个工具,必须首先调用MapX的CreateCustomTool方法,然后在MapX的不同事件中来写一些处理代码,来完成本工作,例如距离量测,可以通过自定义一个线工具:

    Map1.CreateCustomTool RulerToolID, miToolTypeLine, miSizeAllCursor

    然后在MouseMove事件中书写正在变化的距离代码:

        If Button = 1 And Map1.CurrentTool = RulerToolID Then
            Dim X2 As Double
            Dim Y2 As Double
            Map1.ConvertCoord X, Y, X2, Y2, miScreenToMap
            sbStatusBar.SimpleText = Map1.Distance(MouseDownX1, MouseDownY1, X2, Y2)
        End If

    最后在ToolUsed事件中书写最终的距离代码:

        If ToolNum = RulerToolID Then
            sbStatusBar.SimpleText = ""
            MsgBox "Distance: " & Map1.Distance(X1, Y1, X2, Y2)
        End If

    设计一个类模块

    这样做本身没有什么问题,问题是如果有很多业务有关的地图交互代码也必须写在这里,例如在地图上单击定点后弹出对话框,输入相应的信息这样的代码,那么代码就成了很长的If…ElseIf语句的序列,修改维护非常不方便。我们以距离量测为例,来说明如何使用委托的方法,来将这些代码独立成一个类模块。其思路和《应用Visual Basic的事件机制设计可复用的大粒度GIS组件》一文相同。

    首先需要定义需要的变量和对象、事件;需要注意使用WithEvents定义MapX对象,以响应其事件,然后定义一个Connect方法,可以将对象的实例传给这个对象,并做必要的初始化。代码如下:

    Private Const m_RulerToolID = 104 
    Private WithEvents m_MapX As MapXLib.Map

    Public Event PolyRulerToolDistanceChanged(Distance As Double)
    Public Event PolyRulerToolUsed(Distance As Double)

    Public Property Get RulerToolID() As Long
        RulerToolID = m_RulerToolID
    End Property

    Public Function Connect(MapX As MapXLib.Map) As Boolean
        If MapX Is Nothing Then
            Connect = False
        Else
            Set m_MapX = MapX
            m_MapX.CreateCustomTool m_RulerToolID, miToolTypeLine, miSizeAllCursor
        End If
    End Function

    接着在处理其具体操作,例如画线时鼠标移动来改变其当前距离,以及最后的距离:

    Private Sub m_MapX_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
        If Button = 1 And m_MapX.CurrentTool = m_RulerToolID Then
            Dim X2 As Double
            Dim Y2 As Double
            Dim Dis As Double
            m_MapX.ConvertCoord X, Y, X2, Y2, miScreenToMap
            Dis = m_MapX.Distance(m_X1, m_Y1, X2, Y2)
            RaiseEvent RulerToolDistanceChanged(Dis)
        End If
    End Sub

    Private Sub m_MapX_ToolUsed(ByVal ToolNum As Integer, ByVal X1 As Double, ByVal Y1 As Double, ByVal X2 As Double, ByVal Y2 As Double, ByVal Distance As Double, ByVal Shift As Boolean, ByVal Ctrl As Boolean, EnableDefault As Boolean)
        If ToolNum = m_RulerToolID Then
            RaiseEvent RulerToolUsed(m_MapX.Distance(X1, Y1, X2, Y2))
        End If
    End Sub

    类模块使用

    这样就完成了自定义工具的一个类模块的设计,模块使用如下:

    定义:Private WithEvents RulerTool As MapXRulerTool

    在Form_Load中实例化:
    Set RulerTool = New MapXRulerTool
    RulerTool.Connect Me.MapX

    处理其事件:
    Private Sub RulerTool_RulerToolDistanceChanged(Distance As Double)
        frmMain.CommandBars.StatusBar.IdleText = Distance
    End Sub

    Private Sub RulerTool_RulerToolUsed(Distance As Double)
        MsgBox Distance
    End Sub

    总结

    这种方式和思路和Bridge模式类似,但Bridge是将实现分离出去,又有些差别,事件机制本身又是Observer,因此简单的说使用委托机制可能更类似,欢迎批评指正,以提高我们的设计水平。

    代码本身使用的是VB 6,但对其他版本和语言应该是类似的,这些天做项目在使用这个,所以文中代码也就用VB了。

    附:VB 2005代码及UML图

    类的UML图:


    代码:
    Public Class MapXRulerTool

        Private Const _rulerToolID As Integer = 104
        Private Const _polyRulerToolID As Integer = 105

        Private WithEvents _mapX As AxMapXLib.AxMap

        Private _x1 As Double
        Private _y1 As Double

        Public Event RulerToolDistanceChanged(ByVal Distance As Double)
        Public Event RulerToolUsed(ByVal Distance As Double)
        Public Event PolyRulerToolDistanceChanged(ByVal Distance As Double)
        Public Event PolyRulerToolUsed(ByVal Distance As Double)

        Public ReadOnly Property RulerToolID() As MapXLib.ToolConstants
            Get
                RulerToolID = CType(_rulerToolID, MapXLib.ToolConstants)
            End Get
        End Property

        Public ReadOnly Property PolyRulerToolID() As MapXLib.ToolConstants
            Get
                PolyRulerToolID = CType(_polyRulerToolID, MapXLib.ToolConstants)
            End Get
        End Property

        Public Function Connect(ByVal MapX As AxMapXLib.AxMap) As Boolean
            If MapX Is Nothing Then
                Connect = False
            Else
                _mapX = MapX
                _mapX.CreateCustomTool(_rulerToolID, MapXLib.ToolTypeConstants.miToolTypeLine, _
                    MapXLib.CursorConstants.miSizeAllCursor)
                _mapX.CreateCustomTool(_polyRulerToolID, MapXLib.ToolTypeConstants.miToolTypePoly, _
                    MapXLib.CursorConstants.miSizeAllCursor)
            End If
        End Function

        Private Sub _mapX_MouseDownEvent(ByVal sender As Object, ByVal e As AxMapXLib.CMapXEvents_MouseDownEvent) Handles _mapX.MouseDownEvent
            If e.button = 1 And _mapX.CurrentTool = _rulerToolID Then
                ' Place the current screen coordinates in MouseDownX1 and MouseDownY1
                ' Since these points will be used in the Map.Distance call, they
                ' must be in map coordinates, not screen coordinates
                _mapX.ConvertCoord(e.x, e.y, _x1, _y1, MapXLib.ConversionConstants.miScreenToMap)
            End If
        End Sub

        Private Sub _mapX_MouseMoveEvent(ByVal sender As Object, ByVal e As AxMapXLib.CMapXEvents_MouseMoveEvent) Handles _mapX.MouseMoveEvent
            If e.button = 1 And _mapX.CurrentTool = _rulerToolID Then
                Dim X2 As Double
                Dim Y2 As Double
                Dim Dis As Double
                _mapX.ConvertCoord(e.x, e.y, X2, Y2, MapXLib.ConversionConstants.miScreenToMap)
                Dis = _mapX.Distance(_x1, _y1, X2, Y2)
                RaiseEvent RulerToolDistanceChanged(Dis)
            End If
        End Sub

        Private Sub _mapX_PolyToolUsed(ByVal sender As Object, ByVal e As AxMapXLib.CMapXEvents_PolyToolUsedEvent) Handles _mapX.PolyToolUsed
            If e.toolNum = _polyRulerToolID Then
                Dim i As Integer
                Dim DistanceSoFar As Double

                DistanceSoFar = 0.0#

                Dim mPoints As New MapXLib.Points
                mPoints = CType(e.points, MapXLib.Points)

                ' Find the total distance by adding up each of the line segment distances
                If mPoints.Count > 1 Then
                    For i = 2 To mPoints.Count
                        DistanceSoFar = DistanceSoFar + _
                            _mapX.Distance(mPoints.Item(i).X, mPoints.Item(i).Y, _
                            mPoints.Item(i - 1).X, mPoints.Item(i - 1).Y)
                    Next
                End If

                ' Now, we have the total distance along the polyline
                ' If the user is done with the poly-ruler tool, show this distance
                ' in a message box. Otherwise, just show it in the status bar.
                If e.flags = MapXLib.PolyToolFlagConstants.miPolyToolEnd Then
                    RaiseEvent PolyRulerToolUsed(DistanceSoFar)
                Else
                    RaiseEvent PolyRulerToolDistanceChanged(DistanceSoFar)
                End If
            End If
        End Sub

        Private Sub _mapX_ToolUsed(ByVal sender As Object, ByVal e As AxMapXLib.CMapXEvents_ToolUsedEvent) Handles _mapX.ToolUsed
            If e.toolNum = _rulerToolID Then
                RaiseEvent RulerToolUsed(_mapX.Distance(e.x1, e.y1, e.x2, e.y2))
            End If
        End Sub
    End Class


     

  • 相关阅读:
    Oracle适配问题解决
    Oracle12C创建视图权限不足
    Oracle12C配置对外访问
    Oracle12C创建scott账户
    Oracle12C安装配置文档
    Redis适配采坑记
    Redis安装问题解决方案
    Redis Linux 安装部署
    【计网 第四章-2】
    【信息论编码2】测度论
  • 原文地址:https://www.cnblogs.com/maweifeng/p/296332.html
Copyright © 2011-2022 走看看