How to create map pins
At first you will need to create custom pin type (group of pins) using this function:
Lua Code:
  1. ZO_WorldMap_AddCustomPin(pinType, pinTypeAddCallback, pinTypeOnResizeCallback, pinLayoutData, pinTooltipCreator)

pinType = unique string that defines category of your pins eg. "My Addon's Pins"

pinTypeAddCallback = function(pinManager), it is called every time when map is changed and should create pins for given area.

pinTypeOnResizeCallback = function(pinManager, mapWidth, mapHeight), called every time when map is resized (zoomed). Usualy not defined (nil).

pinLayoutData = table with the following keys:
level: required, and must be >2. Pins with higher level will be drawn on the top of pins with lower level.
Example values:
points of interests = 50,
AvA objectives = 50-100
quests = 110
group members = 130
wayshrine = 140
player = 160
texture: required, can be string or function. Function can return just a background texture or background, pulse and glow texture.
Example values:
Lua Code:
  1. "YourAddon/Icons/Icon.dds"
or
Lua Code:
  1. local texture1 = "YourAddon/Icons/Icon1.dds"
  2. local texture2 = "YourAddon/Icons/Icon2.dds"
  3.  
  4. function = GetTexture(pin)
  5.    local pinTypeId, pinTag = pin:GetPinTypeAndTag()
  6.    if pinTag == "some value" then
  7.       return texture1
  8.    else
  9.       return texture2
  10.    end
  11. end
size: texture will be resized to square size*size. If not specified, default value is 20.
Example values:
point of interest = 40
quest pins = 32
player pin = 16
insetX, insetY: size of transparent texture border, used to determine if mouse click hits this pin. No need to specify if you do not plan to handle mouse clicks.
minSize: if not specified, default value is 18.
minAreaSize:
showsPinAndArea: true/false
isAnimated: true/false

pinTooltipCreator = table with the following keys:
creator: function(pin) that creates tooltip - or I should say function that will be called when mouse is over the pin, it does not need to create tooltip.
tooltip: (nilable) tooltip control you want to use.
hasTooltip: (optional), function(pin) which returns true/false to enable/disable tooltip.
If used tooltip is ZO_KeepTooltip, ZO_MapLocationTooltip or InformationTooltip, it will be properly initialized before creator is called and after that will be tooltip anchored to the pinControl. If you use any other tooltip, you have to handle initialization and anchoring in creator or hasTooltip functions.

By default are all custom pins disabled, so the next step is to enable your pins:
Lua Code:
  1. ZO_WorldMap_SetCustomPinEnabled(_G[pinType], true)

Beacause map is not changed when you log into the game or after /reloadui, you have to manually refresh pins for the current map:
Lua Code:
  1. ZO_WorldMap_RefreshCustomPinsOfType(_G[pinType])


Sample code:
Lua Code:
  1. local pinType = "My unique name for pin type"
  2.  
  3. --sample data
  4. local pinData = {
  5.    ["auridon"] = {      --Auridon, Khenarthi's Roost
  6.       ["skywatch_base"] = {
  7.          { x = 0.5, y = 0.6 },
  8.       },      
  9.    },
  10.    ["main"] = {         --Main quest maps
  11.       ["hallsoftorment1_base"] = {
  12.          { x = 0.256, y = 0.361 },
  13.       },
  14.    },
  15. }
  16.  
  17. --sample layout
  18. local pinLayoutData  = {
  19.    level = 50,
  20.    texture = "EsoUI/Art/MapPins/hostile_pin.dds",
  21.    size = 24,
  22. }
  23.  
  24. --tooltip creator
  25. local pinTooltipCreator = {
  26.    creator = function(pin)
  27.       local locX, locY = pin:GetNormalizedPosition()
  28.       InformationTooltip:AddLine(zo_strformat("Position of my pin is: <<1>>,<<2>>", locX*1000, locY*1000))
  29.    end,
  30.    tooltip = InformationTooltip,
  31. }
  32.  
  33. --add callback function
  34. local function pinTypeAddCallback(pinManager)
  35.    --do not create pins if your pinType is not enabled
  36.    if not ZO_WorldMap_IsCustomPinEnabled(_G[pinType]) then return end
  37.    --do not create pins on world, alliance and cosmic maps
  38.    if (GetMapType() > MAPTYPE_ZONE) then return end
  39.  
  40.    local textureName = GetMapTileTexture()
  41.    textureName = string.lower(textureName)
  42.    local _,_,_,zone,subzone = string.find(textureName, "(maps/)([%w%-]+)/([%w%-]+_%w+)")
  43.    local pins
  44.  
  45.    if pinData[zone] and pinData[zone][subzone] then
  46.       pins = pinData[zone][subzone]
  47.    else
  48.       return         --return if no data for current map
  49.    end
  50.  
  51.    for _, pinInfo in ipairs(pins) do
  52.       --ZO_WorldMapPins:CreatePin(pinTypeId, pinTag, locX, locY, areaRadius)
  53.       --pinTag can be anything, but I recommend using table or string with additional info about pin - you can use it later in code
  54.       pinManager:CreatePin(_G[pinType], pinInfo, pinInfo.x, pinInfo.y)
  55.    end
  56. end
  57.  
  58. --resize callback function (usualy just nil)
  59. local function pinTypeOnResizeCallback(pinManager, mapWidth, mapHeight)
  60.    --local mapWidth, mapHeight = ZO_WorldMapContainer:GetDimensions()
  61.    --local currentZoom = ZO_WorldMapZoomSliderButton1.object.value  
  62.    local visibleWidth, visibleHeight = ZO_WorldMapScroll:GetDimensions()
  63.    local currentZoom = mapWidth / visibleWidth
  64.    
  65.    if currentZoom < 1.5 then
  66.       ZO_WorldMap_SetCustomPinEnabled(_G[pinType], false)
  67.    else
  68.       ZO_WorldMap_SetCustomPinEnabled(_G[pinType], true)
  69.    end
  70.  
  71.    ZO_WorldMap_RefreshCustomPinsOfType(_G[pinType])
  72. end
  73.  
  74. local function OnLoad(eventCode, name)
  75.    if name ~= "MyAddon" then return end
  76.  
  77.    --initialize map pins
  78.    ZO_WorldMap_AddCustomPin(pinType, pinTypeAddCallback, pinTypeOnResizeCallback, pinLayoutData, pinTooltipCreator)
  79.    --custom pins are disabled by default, you have to enable them
  80.    ZO_WorldMap_SetCustomPinEnabled(_G[pinType], true)
  81.    --force refresh pins to call pinTypeAddCalback for the current map
  82.    ZO_WorldMap_RefreshCustomPinsOfType(_G[pinType])
  83.  
  84.    EVENT_MANAGER:UnregisterForEvent("MyAddon_OnLoad", EVENT_ADD_ON_LOADED)
  85. end
  86.  
  87. EVENT_MANAGER:RegisterForEvent("MyAddon_OnLoad", EVENT_ADD_ON_LOADED, OnLoad)