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.
Let me walk you through the steps:
The existing code already created a frame for the map window and inserted a clone of an existing map window into it.
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
------------------------------