Module:ResourceMap
Jump to navigation
Jump to search
Documentation
local p = {}
local Strings = mw.loadData('Module:ResourceMap/config')
local function getMarkerTypeInfo(markerType)
return Strings.Markers[markerType] or Strings.Markers.Unrecognised
end
local function getMarkerInfo(node)
return Strings.Markers[node.type] or Strings.Markers[node.rootType] or Strings.Markers.Unrecognised
end
local function getMapSettings(args)
assert(args.id == nil or args.id == mw.text.encode(args.id), 'map ID may not contain any HTML entities or tokens')
return {
args = args,
id = args.id or 'generic',
size = tonumber(args.size) or tonumber(args.mapsize) or 800,
image = args.map or 'Blank.png',
borderCoordTop = tonumber(args.borderCoordTop) or 0,
borderCoordBottom = tonumber(args.borderCoordBottom) or 100,
borderCoordLeft = tonumber(args.borderCoordLeft) or 0,
borderCoordRight = tonumber(args.borderCoordRight) or 100,
legendNotice = args['legend notice'],
legendSpecial = args['legend showSpecialSection'] and Strings.Layers[args['legend showSpecialSection']] or nil,
}
end
local function gatherNodes(args)
local nodes = {}
local rootTypes = {}
local typesSet = {}
for argindex, v in ipairs(args) do
if #v > 0 then
local info = mw.text.split(v, ',', true)
if #info >= 3 then
-- Extract comma-delimited properties
local resourceType = mw.text.trim(info[3])
local lat = tonumber(info[1])
local long = tonumber(info[2])
local customTitle = info[4] and mw.text.trim(info[4]) or ''
if resourceType ~= nil and lat ~= nil and long ~= nil then
-- Pack node information.
local node = {
type = resourceType,
rootType = mw.text.split(resourceType, ' ', true)[1],
lat = lat,
long = long,
customTitle = customTitle,
}
-- Push the node onto the list.
if nodes[resourceType] == nil then
nodes[resourceType] = { node }
if typesSet[node.rootType] == nil then
table.insert(rootTypes, node.rootType)
typesSet[node.rootType] = true
end
else
table.insert(nodes[resourceType], node)
end
else
-- Resource type invalid, latitude invalid, or longitude invalid.
error(string.format('arg#%d: missing resource type, or coordinates are not numbers'))
end
else
-- Not enough comma-delimited values.
error(string.format('arg#%d: at least 3 comma-delimited values are expected', argindex))
end
end
end
return {
rootTypes = rootTypes,
nodes = nodes,
}
end
function p.renderNode(settings, node)
local info = getMarkerInfo(node)
-- Construct tooltip text.
local title = string.format('%s lat %.2f, lon %.2f', info[1], node.lat, node.long)
if node.customTitle ~= nil then
title = title .. ' ' .. node.customTitle
end
-- Get marker size
local markerSize = info[2] or 7
-- Calculate position offsets.
local top = 100 * ((node.lat - settings.borderCoordTop) / (settings.borderCoordBottom - settings.borderCoordTop) - markerSize/(2*settings.size))
local left = 100 * ((node.long - settings.borderCoordLeft) / (settings.borderCoordRight - settings.borderCoordLeft) - markerSize/(2*settings.size))
-- Construct resulting HTML.
return string.format('<div style="left:%.1f%%;top:%.2f%%" title="%s"></div>', left, top, title)
end
function p.renderNodeGroups(settings, nodes)
local html = {}
for resourceType, nodes in pairs(nodes) do
html[#html+1] = '<div class="' .. resourceType .. ' dots">'
for _, node in ipairs(nodes) do
html[#html+1] = p.renderNode(settings, node)
end
html[#html+1] = '</div>'
end
return table.concat(html)
end
local function encodeAsDataAttributes(t)
local out = {}
for key, value in pairs(t) do
if value ~= nil then
if type(value) == 'boolean' then
value = value and '1' or '0'
end
out[#out+1] = 'data-'..key..'="'..value..'"'
end
end
return table.concat(out, ' ')
end
local function renderLegendHeading(text, needsJs)
return table.concat({
'<tr class="no-icon',
needsJs and ' data-map-needs-js' or '',
'">',
'<td colspan="2"><b>',
text,
'</b></td>',
'</tr>',
})
end
local function compareMarkerRootTypesForLegend(a, b)
if Strings.ForceOrder[a] or Strings.ForceOrder[b] then
return (Strings.ForceOrder[a] or 999) < (Strings.ForceOrder[b] or 999)
end
return getMarkerTypeInfo(a)[1] < getMarkerTypeInfo(b)[1]
end
local function getMarkerIconClassDefinitions(settings)
local result = {}
for name, value in pairs(settings.args) do
name = mw.text.split(name, ' ', true)
if #name == 3 and name[1] == 'marker' and name[3] == 'icon' then
local url = mw.text.split(value, '/', true)
assert(#url == 7, 'marker icon value must be a result of {{filepath:...}}')
result[#result+1] = name[2] .. ':' .. url[5] .. '/' .. url[6] .. '/' .. url[7]
end
end
return #result > 0 and table.concat(result, ';') or nil
end
function p.renderLegend(settings, rootTypes)
table.sort(rootTypes, compareMarkerRootTypesForLegend)
local html = {
'<table id="map-legend-'..settings.id..'" class="map-legend" ',
encodeAsDataAttributes {
["marker-icons"] = getMarkerIconClassDefinitions(settings),
},
'>',
}
if settings.legendSpecial then
html[#html+1] = renderLegendHeading(settings.legendSpecial[1], true)
for index, layer in ipairs(settings.legendSpecial) do
if index > 1 then
html[#html+1] = '<tr class="data-map-needs-js">'
html[#html+1] = '<td class="dots"></td>'
html[#html+1] = '<td class="map-legend-checkbox" '
html[#html+1] = encodeAsDataAttributes {
["marker-name"] = layer[1],
}
html[#html+1] = '>'..layer[2]..'</td>'
html[#html+1] = '</tr>'
end
end
end
if #rootTypes > 0 then
html[#html+1] = renderLegendHeading(Strings.Resources)
end
for _, markerType in ipairs(rootTypes) do
local typeInfo = getMarkerTypeInfo(markerType) -- { name, size, colour, border settings }
local label = settings.args['marker ' .. markerType .. ' name']
if label == nil then
label = typeInfo[1]
if typeInfo == Strings.Markers.Unrecognised then
label = label .. ' (' .. markerType .. ')'
end
end
html[#html+1] = '<tr>'
html[#html+1] = '<td class="dots"><div class="legend '..markerType..'"></div></td>'
html[#html+1] = '<td class="map-legend-checkbox" '
html[#html+1] = encodeAsDataAttributes {
["marker-name"] = markerType,
["marker-size"] = typeInfo[2],
["marker-color"] = typeInfo[3],
["marker-border"] = typeInfo[4],
}
html[#html+1] = '>'..label..'</td>'
html[#html+1] = '</tr>'
if settings.args['marker ' .. markerType .. ' note'] then
html[#html+1] = '<tr class="no-icon"><td></td><td>'
html[#html+1] = settings.args['marker ' .. markerType .. ' note']
html[#html+1] = '</td></tr>'
end
end
html[#html+1] = '</table>'
return table.concat(html)
end
function p.renderMapW(f)
local args = f:getParent().args
local settings = getMapSettings(args)
local nodeData = gatherNodes(args)
local html = {
'<div class="data-map-container" id="map-', args.id, '" ',
encodeAsDataAttributes {
["border-top"] = args.borderCoordTop,
["border-left"] = args.borderCoordLeft,
["border-right"] = args.borderCoordRight,
["border-bottom"] = args.borderCoordBottom,
["spawn-data-page-name"] = args.spawnDataPage or nil,
["spawn-data-cache-id"] = args.spawnDataCacheId and tonumber(args.spawnDataCacheId) or nil,
},
'>',
}
html[#html+1] = '<div class="map-legend-container">'
if settings.legendNotice ~= nil then
html[#html+1] = string.format('<div class="map-notice">%s</div>', settings.legendNotice)
end
html[#html+1] = p.renderLegend(settings, nodeData.rootTypes)
html[#html+1] = '</div>'
local maxWidthStyle = 'style="max-width:'..settings.size..'px"'
html[#html+1] = '<table class="wikitable resourcemaptable" '..maxWidthStyle..'>'
html[#html+1] = '<tr><td>'
html[#html+1] = '<div class="map-container" '..maxWidthStyle .. '>'
html[#html+1] = p.renderNodeGroups(settings, nodeData.nodes)
html[#html+1] = '[[File:'..settings.image..'|class=resourcemap|link=]]'
html[#html+1] = '</div>'
html[#html+1] = '</td></tr>'
html[#html+1] = '<tr><td align="middle">' .. (args.caption or '') .. '</td></tr>'
if args.spawnDataPage then
html[#html+1] = '<tr><td align="top">'
html[#html+1] = f:expandTemplate{ title = 'RarityLegend', args = { '330', forInteractiveMap = 'yes' } }
html[#html+1] = '</td></tr>'
end
html[#html+1] = '</table>'
html[#html+1] = '</div>'
return table.concat(html)
end
return p