Peer review of shape flashing function (VB2010)

May 1, 2012 at 9:50 PM

So I have just moved to MW4.8 and realize that many of my old functions just will not work with the new framework. So for MW4.8 I developed a new function to flash a shape much like in other GIS systems. Users find this useful when there are many polygon shapes and they want to locate where one shape is located. Please use this function and report any issues or suggestions on how to improve performance:

    ''' <summary>
    ''' Abel G. Perez
    ''' www.WarpEngine.com
    ''' Apr 5, 2012
    ''' Will simulate flash of a polygon shape by changing its outline and fill color for several seconds and will also
    ''' display a crosshair across the map centered on the shape. Tested with a polygon shapefile but should work with
    ''' lines and points.
    ''' </summary>
    ''' <param name="nCyclesCnt">Number of cycles to turn the shape on and off</param>
    ''' <param name="nLayerHandle">Handle of the layer</param>
    ''' <param name="nShapeIdx">Shape index that will be flashed</param>
    ''' <remarks>Written and tested with VB.NET2005 and MapWindow release 4.8. Will not work with versions below 4.8.
    ''' If using the Utils.Centroid function then it will need a reference to the MapWindow GeoProcessing assembly
    ''' typically found at C:\Program Files\MapWindow\MapWinGeoProc.dll.
    ''' Based in part on these forum posts:
    ''' http://www.mapwindow.org/phorum/read.php?3,513,515#msg-515
    ''' http://www.mapwindow.org/phorum/read.php?3,22779
    ''' Needs some work as I couldnt get the shape to flash on and off without refreshing the map. Probably overkill
    ''' with the Redraw and the DoEvents.
    ''' </remarks>
    Public Sub FlashShape(ByVal nCyclesCnt As Integer, ByVal nLayerHandle As Integer, ByVal nShapeIdx As Integer)
        '*********************************************************************************************************************
        'determine what kind of layer it is and if not a shapefile then exit out of the routine
        '*********************************************************************************************************************
        Select Case legMain.Layers.ItemByHandle(nLayerHandle).Type
            Case MapWindow.Interfaces.eLayerType.PointShapefile, MapWindow.Interfaces.eLayerType.LineShapefile, MapWindow.Interfaces.eLayerType.PolygonShapefile
                'these are ok so keep on
            Case Else
                'these are not so bail
                Return
        End Select

        '*********************************************************************************************************************
        'get the shapefile object of the layer handle. we assume its always a shapefile and we already checked that above. then
        'we check to see if its a polygon shapefile.
        '*********************************************************************************************************************
        Dim sf As MapWinGIS.Shapefile = mapMain.get_Shapefile(nLayerHandle)
        If IsPolygonShapefile(nLayerHandle) = False Then Exit Sub

        '*********************************************************************************************************************
        'now get the shape of interest in the layer and save the category that it originally belongs to. we will set it back to
        'this category once we are done flashing. a category of -1 means it does not have a category.
        '*********************************************************************************************************************
        Dim shp As MapWinGIS.Shape = sf.Shape(nShapeIdx)
        Dim cOrigIdx As Integer = sf.ShapeCategory(nShapeIdx)

        '*********************************************************************************************************************
        'get the center of the shape as a point. As of MW4.8 the shape class has a centroid property and we dont have to use the
        'Utils library
        'http://www.mapwindow.org/wiki/index.php/MapWinGeoProc:Utils_Centroid
        'http://www.mapwindow.org/documentation/mapwingis4.8/class_shape.html#a05f47447cfd056b28a579ba4a78f7bd0
        '*********************************************************************************************************************
        Dim pt As New MapWinGIS.Point
        'pt = MapWinGeoProc.Utils.Centroid(shp)
        pt = shp.Centroid

        '*********************************************************************************************************************
        'get the extents of the map
        '*********************************************************************************************************************
        Dim ext As MapWinGIS.Extents = CType(mapMain.Extents, MapWinGIS.Extents)

        '*********************************************************************************************************************
        'now draw two fat lines, one vertical and one horizontal to focus the center of the shape
        '*********************************************************************************************************************
        Dim hndLineDrawing As Integer = mapMain.NewDrawing(MapWinGIS.tkDrawReferenceList.dlSpatiallyReferencedList)
        '/// each line is extended 5 degrees to each side of the center of the polygon
        'mapMain.DrawLine(pt.x, pt.y - 5, pt.x, pt.y + 5, 2, ColorToUInteger(Color.Cyan)) 'horizontal
        'mapMain.DrawLine(pt.x - 5, pt.y, pt.x + 5, pt.y, 2, ColorToUInteger(Color.Cyan)) 'vertical
        '/// or extend the line from each side of the map extents to center of the polygon
        mapMain.DrawLine(pt.x, ext.yMin, pt.x, ext.yMax, 4, ColorToUInteger(Color.Cyan)) 'horizontal
        mapMain.DrawLine(ext.xMin, pt.y, ext.xMax, pt.y, 4, ColorToUInteger(Color.Cyan)) 'vertical

        Threading.Thread.Sleep(100)
        Application.DoEvents()

        '*********************************************************************************************************************
        'add a category to the shapefile with some default drawing properties. a fill color of green and an outline color of cyan.
        '*********************************************************************************************************************
        Dim cat As MapWinGIS.ShapefileCategory = sf.Categories.Add("FlashShape_")   'name of category should be random as it could conflict with an existing category
        Dim cNewIdx As Integer = sf.Categories.Count - 1                            'this is the index of the category since we just added it. indexes are zero based
        Dim opts As New MapWinGIS.ShapeDrawingOptions
        opts.FillColor = ColorToUInteger(Color.Green)
        opts.LineWidth = 2
        opts.LineColor = ColorToUInteger(Color.Cyan)
        cat.DrawingOptions = opts

        '*********************************************************************************************************************
        'assign the new category index to our shape. this one shape now will take on the properties of our category.
        '*********************************************************************************************************************
        sf.ShapeCategory(nShapeIdx) = cNewIdx

        '*********************************************************************************************************************
        'now we change the drawing options of the category. we color the polygon green, pause, then color the polygon red.
        'And since the category is connected to the shape then it should simulate a flash for a couple cycles, each one being
        'about 300*2 milliseconds in length. i experimented with various ways to display the flashing but in the end it was a
        'combination of applying the color, redrawing the map, and allowing the application to respond that seemed to work.
        'it may be slow for maps with large amounts of data as it has to redraw in several loops. Overkill on DoEvents?
        '*********************************************************************************************************************
        For i As Integer = 1 To nCyclesCnt
            'the first pass colors the shape green and leaves it like that for 300 milliseconds
            opts.FillColor = ColorToUInteger(Color.Green)
            Application.DoEvents()
            mapMain.Redraw()
            Application.DoEvents()
            Threading.Thread.Sleep(300)
            Application.DoEvents()

            'the second pass colors the shape red and leaves it like that for 300 milliseconds
            opts.FillColor = ColorToUInteger(Color.Red)
            Application.DoEvents()
            mapMain.Redraw()
            Application.DoEvents()
            Threading.Thread.Sleep(300)
            Application.DoEvents()
        Next i

        '*********************************************************************************************************************
        'now we set the shape's category back to the original since we are done flashing. this should set the shape's drawing
        'options back to its original. also remove the category we created since we dont need it anymore.
        '*********************************************************************************************************************
        sf.ShapeCategory(nShapeIdx) = cOrigIdx 'back to its original category (a -1 means it had no category to begin with)
        Threading.Thread.Sleep(100)
        Application.DoEvents()
        sf.Categories.Remove(cNewIdx)
        'sf.Categories.Clear() 'this will remove ALL categories and the user may already have some existing in their layer
        'cat.DrawingOptions = Nothing
        'opts = Nothing 'not sure why needed but if not then prior shapes also get the drawing options
        'sf.Categories.ApplyExpressions()

        '*********************************************************************************************************************
        'and finally clear the line drawings
        '*********************************************************************************************************************
        Threading.Thread.Sleep(100)
        Application.DoEvents()
        mapMain.ClearDrawing(hndLineDrawing)
        mapMain.Redraw()
    End Sub