MapInfo Pro Developers User Group

 View Only

MapBasic Monday: Add a Legend to a Layout Through MapBasic

  • 1.  MapBasic Monday: Add a Legend to a Layout Through MapBasic

    Employee
    Posted 3 hours ago
      |   view attached

    Last week, @Mayca González Pérez raised a question about inserting legends into his layout using MapBasic.

    I gave a generic answer, but it can be a bit hard to adapt it to fit his MapBasic code and variable names. So today, I decided to adapt my code to his.

    The resulting layout looks like this.

    image
    Let me walk you through the steps:

    Getting the Map Window Frame ID

    The existing code already created a frame for the map window and inserted a clone of an existing map window into it.
    'Insert Map into Layout
    Create Frame Into Window nLayoutID (margen, margen)  (ancho-franja, alto-banda)
        'Pen (1,2,0) Brush (2, 16777215, 16777215)
        From Window nMapID
        FillFrame On
    
    nMapIDInLayout = WindowID(0)

    I just added a single line of code to this to capture the ID of the frame holding the main map

    nMapFrameID	= LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS)

    I need this later on to be able to insert the legends over this frame.

    Capture the Location of the Map Window Frame

    I use the Frame ID that I captured above to now find the location of the top left corner of this frame. I also capture the heigh and width of the frame.

    '## Finding the top left corner of the map frame and inserting the legends from this location
    fFrameX		= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_POS_X)
    fFrameY		= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_POS_Y)
    fFrameWidth	= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_WIDTH)
    fFrameHeight	= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_HEIGHT)
    
    fFramePositionY	= fFrameY

    These values will be used to position the legends as I start inserting these.

    This also means that if you want to place your legends elsewhere on the layout, you can set a different position here.

    Inserting the Legends

    Now I loop over the layers in the main map window and create a legend for each of these, as long as they are normal layers or thematic layers.

    For the first layer, I use Create Designer Legend:

    Create Designer Legend
    	From Window nMapIDInLayout
    	Default Frame Style "#" Font ("Arial",0,8,0)
    	Custom
    	Frame From Layer nLayer
    		Position (fFrameX, fFramePositionY) Units "in"
    		Using Column Object Label default
    nLegendWID =  LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_LEGEND_DESIGNER_WINDOW)

    I set the Default Frame Style, which will affect the other legends created as well.

    I use the current values for the legends to position the legend, and I set it to use the Object to control the style.

    Finally, I read the Legend Window ID, so that I can reference this for the remaining layers.

    For the remaining layers, I use Add Designer Frame to add to the existing frame; otherwise, it uses the same legend settings:

    Add Designer Frame
    	Window nLegendWID
    	Custom
    	Frame From Layer nLayer
    		Position (fFrameX, fFramePositionY) Units "in"
    		Using Column Object Label default

    Finally, I capture and calculate the position of the next frame and the maximum width of the frames so far.

    fFramePositionY	= fFramePositionY + LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_HEIGHT)
    fLegendWidth		= LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_WIDTH)
    fLegendWidthMax	= Maximum(fLegendWidthMax, fLegendWidth)
    

    Here is the full source code related to inserting legends:

    '## Inserting a legend for each layer in the map
    nNumLayers	= MapperInfo(nMapIDInLayout, MAPPER_INFO_LAYERS)
    For nLayer = 1 to nNumLayers
    	If LayerInfo(nMapIDInLayout, nLayer, LAYER_INFO_TYPE) In (LAYER_INFO_TYPE_NORMAL, LAYER_INFO_TYPE_THEMATIC) Then
    		sLayerAlias	= LayerInfo(nMapIDInLayout, nLayer, LAYER_INFO_ALIAS)
    		Print nLayer & ". Layer Alias:  " & sLayerAlias
    		'Create Designer Legend
    		'	From Window 3018
    		'	Portrait
    		'	Default Frame Style "Stores" Font ("Arial",0,8,0)
    		'	Frame
    		'		From Layer 1 Using column object label default
    
    		If nLegendWID = 0 Then
    			Create Designer Legend
    				From Window nMapIDInLayout
    				Default Frame Style "#" Font ("Arial",0,8,0)
    				Custom
    				Frame From Layer nLayer
    					Position (fFrameX, fFramePositionY) Units "in"
    					Using Column Object Label default
    			nLegendWID =  LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_LEGEND_DESIGNER_WINDOW)
    		Else
    			Add Designer Frame
    				Window nLegendWID
    				Custom
    				Frame From Layer nLayer
    					Position (fFrameX, fFramePositionY) Units "in"
    					Using Column Object Label default
    		End If
    
    		fFramePositionY	= fFramePositionY + LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_HEIGHT)
    		fLegendWidth		= LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_WIDTH)
    		fLegendWidthMax	= Maximum(fLegendWidthMax, fLegendWidth)
    	End If
    Next  'nLayer

    I ned the creation of the legends by inserting a rectangle under the legends so that the map underneath is hidden.

    '## Create frame to sit behind legend and create white space
    Create Rect (fFrameX, fFrameY) (fFrameX + fLegendWidthMax, fFramePositionY)
    	Pen (1,2,0)
    	Brush (2,16777215,16777215)
    	Priority 1

    Variables

    I also added several variables, which I merged into the existing variables of the program.

    Below, you can see the variables that I added without the existing variables:

    Dim nMapFrameID, nLayer, nNumLayers, nLegendWID As Integer
    Dim sLayerAlias as String
    Dim	fFrameX, fFrameY, fFramePositionY, fFrameWidth, fFrameHeight, fLegendWidth, fLegendWidthMax As Float

    The Final Source Code

    Here is the full adapted sourcecode. I have also attached the full source code in the form of a zipped mb file.

    I had to comment out a few calls to additional procedures that I didn't have access to, to make the program run.

    I also commented out the creation of the logo map and the north arrow map. I would suggest inserting the logo as an image, as that is now supported. The north arrow can be inserted as a point with a north arrow symbol if you prefer.

    Include "MapBasic.def"
    
    Declare Sub Main
    Declare Sub TEMATICO_INFRAESTR_VIARIA
    
    Dim gblNOMBRE_MUNICIPIO, gblNOMBRE_NUCLEO As String
    Dim num_win_localizacion As Integer
    
    Sub Main
    	Call TEMATICO_INFRAESTR_VIARIA
    End Sub
    
    Sub TEMATICO_INFRAESTR_VIARIA
    
    OnError GoTo errorTematicoViaria
    
    '1) Cargar configuración guardada
    '2) Cargar parámetros geométricos
    '3) Crear layout con tamaño correcto
    '4) Aplicar formato impresora
    '5) Insertar frames usando coordenadas parametrizadas
    
    Dim nMapID, nMapIDInLayout, nLayoutID, nLegendID, nMapFrameID, nLayer, nNumLayers, i, nLegendWID As Integer
    Dim strTitulo, strStatement, cmdstr, str_tit_principal, str_tit_leyenda, sLayerAlias as String
    Dim	fFrameX, fFrameY, fFramePositionY, fFrameWidth, fFrameHeight, fLegendWidth, fLegendWidthMax As Float
    
    strTitulo = "MUNICIPIO: "+gblNOMBRE_MUNICIPIO+chr$(13)+"NÚCLEO:"+gblNOMBRE_NUCLEO
    'Call CARGAR_PARAM_TEMATICOS
    
    'Set Printer Name gblThematicPrinter
    
    ''##### ASIGNANDO NOMBRE DEL MUNICIPIO ENCUESTADO Y RUTA DE ARCHIVOS ###############
    'Call TEMATICO_CARGAR_PARAMETROS
    
    str_tit_principal = WindowInfo(FrontWindow(), WIN_INFO_NAME)
    
    '**Let's find a map window to insert
    For i = 1 To NumWindows()
        If WindowInfo(WindowID(i), WIN_INFO_TYPE) = WIN_MAPPER Then
          nMapID = WindowID(i)
          Exit For
        End If
    Next
    Print "Map: " & WindowInfo(nMapID, WIN_INFO_NAME)
    num_win_localizacion = nMapID
    
    Dim strMapName As String
    Dim strMunicipio As String
    Dim strNucleo As String
    Dim strTematico As String
    Dim posGuion As Integer
    Dim posAsterisco As Integer
    
    strMapName = WindowInfo(nMapID, WIN_INFO_NAME)
    
    posGuion = InStr(1, strMapName, " - ")
    posAsterisco = InStr(1, strMapName, "*")
    
    If posGuion > 0 Then
        strMunicipio = Left$(strMapName, Len(posGuion) - 1)
    End If
    
    If posAsterisco > 0 Then
        strNucleo = Mid$(strMapName, posGuion + 3, posAsterisco - (Len(posGuion) + 3))
        strTematico = Mid$(strMapName, posAsterisco + 1, Len(strMapName) - posAsterisco)
    End If
    
    'Create Layout window
    Layout Designer
        Position (0.0,0.0) Units "in"
    '    Width pWidth  Height pHeight  Units "in"
    
    nLayoutID = FrontWindow()
    Set Layout
        Window nLayoutID
        Extents To Fit Zoom 100
    
    Set CoordSys Layout Units "in"
    Set Paper Units "In"
    
    'Call TEMATICO_FORMATO_IMPRIMIR_PAPEL
    
    Dim ancho As Float
    Dim alto As Float
    
    ancho = LayoutInfo(nLayoutID, LAYOUT_INFO_WIDTH)
    alto  = LayoutInfo(nLayoutID, LAYOUT_INFO_HEIGHT)
    
    Dim margen As Float
    Dim franja As Float
    Dim banda As Float
    
    margen = ancho * 0.03          ' 3% margen
    franja = ancho * 0.18          ' 18% franja derecha
    banda  = alto * 0.10           ' 10% banda superior
    
    'Insert Map into Layout
    Create Frame Into Window nLayoutID (margen, margen)  (ancho-franja, alto-banda)
        'Pen (1,2,0) Brush (2, 16777215, 16777215)
        From Window nMapID
        FillFrame On
    
    nMapIDInLayout = WindowID(0)
    nMapFrameID	= LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS)
    'num_win_principal = nMapID
    
    'Call TEMATICO_CAPAS_AUXILIARES
    
    Dim colX1 As Float
    Dim colX2 As Float
    Dim zonaY1 As Float
    Dim zonaY2 As Float
    Dim bloqueAltura As Float
    Dim logoY1 As Float
    Dim logoY2 As Float
    Dim textoY1 As Float
    Dim textoY2 As Float
    
    logoY1 = zonaY1
    logoY2 = zonaY2 - 2*bloqueAltura
    
    textoY1 = logoY2
    textoY2 = logoY2 + (bloqueAltura * 0.40)
    
    colX1 = ancho - franja + margen/2
    colX2 = ancho - margen/2
    
    zonaY1 = margen
    zonaY2 = alto - banda
    
    bloqueAltura = (zonaY2 - zonaY1) / 3
    
    Dim alturaTotal As Float
    Dim hLoc As Float
    Dim hNorte As Float
    Dim hLogo As Float
    Dim hTexto As Float
    
    colX1 = ancho - franja + margen/2
    colX2 = ancho - margen/2
    
    zonaY1 = margen
    zonaY2 = alto - banda
    
    alturaTotal = zonaY2 - zonaY1
    
    hLoc   = alturaTotal * 0.35
    hNorte = alturaTotal * 0.20
    hLogo  = alturaTotal * 0.25
    hTexto = alturaTotal * 0.20
    
    Dim yActual As Float
    yActual = zonaY2
    
    'LOCALIZACIÓN
    'Create Frame Into Window nLayoutID (colX1, zonaY2 - 2*bloqueAltura)  (colX2, zonaY2)
    Create Frame Into Window nLayoutID (colX1, yActual - hLoc) (colX2, yActual)
        Pen (1,2,0)
        Brush (2,16777215,16777215)
        From Window num_win_localizacion
        FillFrame On
    
    yActual = yActual - hLoc
    
    'NORTE
    'Create Frame Into Window nLayoutID (colX1, zonaY2 - 2*bloqueAltura)  (colX2, zonaY2 - bloqueAltura)
    'Create Frame Into Window nLayoutID (colX1, yActual - hNorte) (colX2, yActual)
    '    Pen (1,2,0)
    '    Brush (2,16777215,16777215)
    '    From Window num_win_norte
    '    FillFrame On
    'yActual = yActual - hNorte
    
    'LOGO
    'Create Frame Into Window nLayoutID  (colX1, zonaY1) (colX2, zonaY2 - 2*bloqueAltura)
    'Create Frame Into Window nLayoutID (colX1, yActual - hLogo) (colX2, yActual)
    '    Pen (1,2,0)
    '    Brush (2,16777215,16777215)
    '    From Window num_win_logo
    '    FillFrame On
    
    yActual = yActual - hLogo
    'MUNICIPIO
    
    Create Rect Into Window nLayoutID (colX1, zonaY1) (colX2, yActual)
        Pen (1,2,0)
        Brush (2,16777215,16777215)
    
    Create Text Into Window nLayoutID
        "MAPA TEMÁTICO: " + UCase$(strTematico) + Chr$(13) + Chr$(13) +
         "MUNICIPIO: " + gblNOMBRE_MUNICIPIO + Chr$(13) +
        "NÚCLEO: " + gblNOMBRE_NUCLEO
        (colX1, zonaY1) (colX2, yActual)
        Font ("Arial",1,9,1)
        Justify Center
    
    'Set Map Window nMapIDInLayout Zoom Entire Layer Selection
    If WindowInfo(nMapIDInLayout, WIN_INFO_TYPE) <> WIN_MAPPER Then
        Note "La ventana activa no es un mapa"
        Exit Sub
    End If
    
    '## Finding the top left corner of the map frame and inserting the legends from this location
    fFrameX		= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_POS_X)
    fFrameY		= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_POS_Y)
    fFrameWidth	= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_WIDTH)
    fFrameHeight	= LayoutItemInfo(nLayoutID, nMapFrameID, LAYOUT_ITEM_INFO_HEIGHT)
    
    fFramePositionY	= fFrameY
    
    '## INserting a legend for each layer in the map
    nNumLayers	= MapperInfo(nMapIDInLayout, MAPPER_INFO_LAYERS)
    For nLayer = 1 to nNumLayers
    	If LayerInfo(nMapIDInLayout, nLayer, LAYER_INFO_TYPE) In (LAYER_INFO_TYPE_NORMAL, LAYER_INFO_TYPE_THEMATIC) Then
    		sLayerAlias	= LayerInfo(nMapIDInLayout, nLayer, LAYER_INFO_ALIAS)
    		Print nLayer & ". Layer Alias:  " & sLayerAlias
    		'Create Designer Legend
    		'	From Window 3018
    		'	Portrait
    		'	Default Frame Style "Stores" Font ("Arial",0,8,0)
    		'	Frame
    		'		From Layer 1 Using column object label default
    
    		If nLegendWID = 0 Then
    			Create Designer Legend
    				From Window nMapIDInLayout
    				Default Frame Style "#" Font ("Arial",0,8,0)
    				Custom
    				Frame From Layer nLayer
    					Position (fFrameX, fFramePositionY) Units "in"
    					Using Column Object Label default
    			nLegendWID =  LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_LEGEND_DESIGNER_WINDOW)
    		Else
    			Add Designer Frame
    				Window nLegendWID
    				Custom
    				Frame From Layer nLayer
    					Position (fFrameX, fFramePositionY) Units "in"
    					Using Column Object Label default
    		End If
    
    		fFramePositionY	= fFramePositionY + LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_HEIGHT)
    		fLegendWidth		= LayoutItemInfo(nLayoutID, LayoutInfo(nLayoutID, LAYOUT_INFO_NUM_ITEMS), LAYOUT_ITEM_INFO_WIDTH)
    		fLegendWidthMax	= Maximum(fLegendWidthMax, fLegendWidth)
    	End If
    Next  'nLayer
    
    '## Create frame to sit behind legend and create white space
    Create Rect (fFrameX, fFrameY) (fFrameX + fLegendWidthMax, fFramePositionY)
    	Pen (1,2,0)
    	Brush (2,16777215,16777215)
    	Priority 1
    
    DONE:
      Exit Sub
    
    errorTematicoViaria:
      Note "ERROR:" + Chr$(13) + Error$()
      Exit Sub
    
    End Sub


    ------------------------------
    Peter Horsbøll Møller
    Principal Presales Consultant | Distinguished Engineer
    Precisely | Trust in Data
    ------------------------------

    Attachment(s)

    zip
    MaycaLayoutWithLegend.zip   2 KB 1 version