Results 1 to 5 of 5

Thread: Using an image as a map

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2008
    Location
    Zeist, The Netherlands
    Posts
    266

    Using an image as a map

    This is my first CodeBank entry, I hope it's useful.

    Here is the code for an ImageMap control that inherits from Panel. You can assign an image to be displayed to the BackgroundImage property, and an image with a color map to the ColorMap property. When you have done this, on top of the normal MouseMove and MouseClick events, the control will also raise the ImageMouseMove and ImageClicked events that return the color of the color map at the location indicated by the mouse as an additional parameter.

    The ImageMap control also supports zooming by scrolling the mouse wheel. The properties LinearZoom (a fixed percentage instead of the default increasing percentage) and ZoomSpeed control the zooming. It also has a property called ZoomFactor to set it directly.
    Finally the image can be dragged in any direction to scroll to another part of the image.

    I may try to develop it further to support placing tokens on the map, so it can be used for a board game or something like that.

    Code:
    Public Class ImageMap
       Inherits Panel
    
       Private _BaseImage As Image
       Private _ColorMap As Bitmap
       Private _ZoomFactor As Double = 1
       Private _LinearZoom As Boolean = False
       Private _ZoomSpeed As Integer = 20
       Private _MinZoomFactor As Double
    
       Private _TopLeft As New Point(0, 0)
       Private _CurrPos As New Point(0, 0)
       Private _Dragging As Boolean = False
       Private _DragStart As Point
       Private _TopLeftDrag As Point
    
       Public Sub New()
          Me.DoubleBuffered = True
       End Sub
       Public Sub New(ByVal BackgroundImage As Image, ByVal ColorMap As Bitmap)
          Me.DoubleBuffered = True
          Me.BackgroundImage = BackgroundImage
          Me.ColorMap = ColorMap
       End Sub
       Public Shadows Property BackgroundImage As Image
          Get
             Return _BaseImage
          End Get
          Set(ByVal value As Image)
             _BaseImage = value
             SetMinZoomFactor()
             Me.Invalidate()
          End Set
       End Property
       Public Property ColorMap As Bitmap
          Get
             Return _ColorMap
          End Get
          Set(ByVal value As Bitmap)
             _ColorMap = value
          End Set
       End Property
       Public Property ZoomFactor As Double
          Get
             Return _ZoomFactor
          End Get
          Set(ByVal value As Double)
             value = Math.Round(value, 2)
             If value < _MinZoomFactor Then value = _MinZoomFactor
             _TopLeft.X = CInt(Math.Round(_TopLeft.X + _CurrPos.X / _ZoomFactor - _CurrPos.X / value))
             _TopLeft.Y = CInt(Math.Round(_TopLeft.Y + _CurrPos.Y / _ZoomFactor - _CurrPos.Y / value))
             ValidateTopLeft()
             If _ZoomFactor <> value Then
                _ZoomFactor = value
                Me.Invalidate()
             End If
          End Set
       End Property
       Public Property LinearZoom As Boolean
          Get
             Return _LinearZoom
          End Get
          Set(ByVal value As Boolean)
             _LinearZoom = value
          End Set
       End Property
       Public Property ZoomSpeed As Integer
          Get
             Return _ZoomSpeed
          End Get
          Set(ByVal value As Integer)
             _ZoomSpeed = value
          End Set
       End Property
    
       Public Event ImageClicked(ByVal sender As Object, ByVal e As MouseEventArgs, ByVal ReturnedColor As Color)
       Protected Overrides Sub OnMouseClick(ByVal e As System.Windows.Forms.MouseEventArgs)
          MyBase.OnMouseClick(e)
          Dim Pnt As Point = FindRealPoint(New Point(e.X, e.Y))
          If Not _ColorMap Is Nothing AndAlso Pnt.X < _ColorMap.Width AndAlso Pnt.Y < _ColorMap.Height Then
             RaiseEvent ImageClicked(Me, e, _ColorMap.GetPixel(Pnt.X, Pnt.Y))
          Else
             RaiseEvent ImageClicked(Me, e, Nothing)
          End If
       End Sub
    
       Public Event ImageMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs, ByVal ReturnedColor As Color)
       Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
          If _Dragging Then
             _TopLeft.X = _TopLeftDrag.X - CInt((e.X - _DragStart.X) / _ZoomFactor)
             _TopLeft.Y = _TopLeftDrag.Y - CInt((e.Y - _DragStart.Y) / _ZoomFactor)
             ValidateTopLeft()
             Me.Invalidate()
          Else
             _CurrPos.X = e.X
             _CurrPos.Y = e.Y
             MyBase.OnMouseMove(e)
             Dim Pnt As Point = FindRealPoint(New Point(e.X, e.Y))
             If Not _ColorMap Is Nothing AndAlso Pnt.X > -1 AndAlso Pnt.X < _ColorMap.Width AndAlso Pnt.Y > -1 AndAlso Pnt.Y < _ColorMap.Height Then
                RaiseEvent ImageMouseMove(Me, e, _ColorMap.GetPixel(Pnt.X, Pnt.Y))
             Else
                RaiseEvent ImageMouseMove(Me, e, Nothing)
             End If
          End If
    
       End Sub
    
       Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
          Dim dstRect, srcRect As Rectangle
          srcRect = New Rectangle(_TopLeft.X, _TopLeft.Y, CInt(Me.Width / _ZoomFactor), CInt(Me.Height / _ZoomFactor))
          dstRect = New Rectangle(New Point(0, 0), Me.Size)
          e.Graphics.DrawImage(_BaseImage, dstRect, srcRect, GraphicsUnit.Pixel)
       End Sub
       Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
          MyBase.OnMouseDown(e)
          _Dragging = True
          _DragStart = New Point(e.X, e.Y)
          _TopLeftDrag = _TopLeft
       End Sub
       Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
          MyBase.OnMouseUp(e)
          _Dragging = False
          _DragStart = Nothing
          _TopLeftDrag = Nothing
       End Sub
       Protected Overrides Sub OnMouseWheel(ByVal e As System.Windows.Forms.MouseEventArgs)
          MyBase.OnMouseWheel(e)
          Select Case _LinearZoom
             Case True
                ZoomFactor = _ZoomFactor + (e.Delta / 120 * _ZoomSpeed / 100)
             Case False
                ZoomFactor = _ZoomFactor + (e.Delta / 120 * Math.Floor(_ZoomFactor + 1) * _ZoomSpeed / 400)
          End Select
       End Sub
       Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
          MyBase.OnResize(e)
          SetMinZoomFactor()
          ValidateTopLeft()
          Me.Invalidate()
       End Sub
       Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
          MyBase.Select()
          MyBase.OnMouseEnter(e)
       End Sub
    
       Private Function FindRealPoint(ByVal Pnt As Point) As Point
          Dim Pnt2 As New Point
          Pnt2.X = CInt(_TopLeft.X + Pnt.X / _ZoomFactor)
          Pnt2.Y = CInt(_TopLeft.Y + Pnt.Y / _ZoomFactor)
          Return Pnt2
       End Function
       Private Function IsOnScreen(ByVal Pnt As Point) As Boolean
          If _ZoomFactor > 1 Then
             Dim OnScreen As New Rectangle(_TopLeft, New Size(CInt(Me.Width / _ZoomFactor), CInt(Me.Height / _ZoomFactor)))
             Return OnScreen.Contains(Pnt)
          Else
             Return True
          End If
       End Function
    
       Private Sub SetMinZoomFactor()
          _MinZoomFactor = Math.Round(Math.Min(Me.Width / _BaseImage.Width, Me.Height / _BaseImage.Height), 2)
          If _ZoomFactor < _MinZoomFactor Then ZoomFactor = _MinZoomFactor
       End Sub
       Private Sub ValidateTopLeft()
          If Me.Width / _ZoomFactor >= _BaseImage.Width Then
             _TopLeft.X = Math.Max(_TopLeft.X, CInt(_BaseImage.Width - Me.Width / _ZoomFactor))
             _TopLeft.X = Math.Min(_TopLeft.X, 0)
          Else
             _TopLeft.X = Math.Max(_TopLeft.X, 0)
             _TopLeft.X = Math.Min(_TopLeft.X, CInt(_BaseImage.Width - Me.Width / _ZoomFactor))
          End If
          If Me.Height / _ZoomFactor > _BaseImage.Height Then
             _TopLeft.Y = Math.Max(_TopLeft.Y, CInt(_BaseImage.Height - Me.Height / _ZoomFactor))
             _TopLeft.Y = Math.Min(_TopLeft.Y, 0)
          Else
             _TopLeft.Y = Math.Max(_TopLeft.Y, 0)
             _TopLeft.Y = Math.Min(_TopLeft.Y, CInt(_BaseImage.Height - Me.Height / _ZoomFactor))
          End If
       End Sub
    
    End Class

  2. #2
    Fanatic Member BlindSniper's Avatar
    Join Date
    Jan 2011
    Location
    South Africa
    Posts
    865

    Re: Using an image as a map

    Just A few tips to make it designer friendly
    Code:
        Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            Dim dstRect, srcRect As Rectangle
            srcRect = New Rectangle(_TopLeft.X, _TopLeft.Y, CInt(Me.Width / _ZoomFactor), CInt(Me.Height / _ZoomFactor))
            dstRect = New Rectangle(New Point(0, 0), Me.Size)
            If Me.BackgroundImage IsNot Nothing Then 'add this
                e.Graphics.DrawImage(_BaseImage, dstRect, srcRect, GraphicsUnit.Pixel)
            End If 'and this
    
        End Sub
    and
    Code:
        Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
            MyBase.OnResize(e)
            If Me.BackgroundImage IsNot Nothing Then 'add this
                SetMinZoomFactor()
                ValidateTopLeft()
                Me.Invalidate()
            End If 'add this
    
        End Sub
    and
    Code:
        Public Property ZoomFactor As Double
            Get
                Return _ZoomFactor
            End Get
            Set(ByVal value As Double)
                value = Math.Round(value, 2)
                If value < _MinZoomFactor Then value = _MinZoomFactor
                _TopLeft.X = CInt(Math.Round(_TopLeft.X + _CurrPos.X / _ZoomFactor - _CurrPos.X / value))
                _TopLeft.Y = CInt(Math.Round(_TopLeft.Y + _CurrPos.Y / _ZoomFactor - _CurrPos.Y / value))
                If Me.BackgroundImage IsNot Nothing Then 'add this
                    ValidateTopLeft()
                End If 'add this
    
                If _ZoomFactor <> value Then
                    _ZoomFactor = value
                    Me.Invalidate()
                End If
            End Set
        End Property
    Good code, but for the people like me that doesn't actually know what a color map is, you can think of it as a color picker, but obviously this is more powerful.
    Last edited by BlindSniper; Sep 17th, 2011 at 01:21 PM.

    Useful CodeBank Entries of mine
    Expand Function
    Code Compiler
    Sudoku Solver
    HotKeyHandler Class

    Read this to get Effective help on VBForums
    Hitchhiker's Guide to Getting Help at VBF

  3. #3

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2008
    Location
    Zeist, The Netherlands
    Posts
    266

    Re: Using an image as a map

    Good additions, thank you!
    I built the control to fit the needs of my project, and tested it with the correct implementation. I did not consider that anyone might forget to fill the BackgroundImage property.

    And I thought my description of the events (specifically 'the control will also raise the ImageMouseMove and ImageClicked events that return the color of the color map at the location indicated by the mouse') would be enough explanation, but I guess it is not. I don't know what a color picker is, but I'm sure it will make things clearer for people that do.

  4. #4
    New Member
    Join Date
    Sep 2011
    Posts
    1

    Re: Using an image as a map

    Have you Got this program?

  5. #5

    Thread Starter
    Hyperactive Member
    Join Date
    Mar 2008
    Location
    Zeist, The Netherlands
    Posts
    266

    Re: Using an image as a map

    I attached a new version of the ImageMap control. I now realize what you meant by a color picker, and that's not really it. What this control does is show you an image, and when you move over it, or click it, it raises an event that has as an attribute the color of the pixel at the same location but in ANOTHER image. This way, you can determine in which area of the original image the mouse pointer was when the event was raised. Useful if you want to create an interactive map of a country for example.

    The new version now supports placing images or text in your background image by using the AddImageToken and AddTextToken methods. If anyone needs more info on how to use it, just post here.
    Attached Files Attached Files

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width