Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa
Apparenza
Po' criari la ducumintazzioni di stu mòdulu nta Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa/doc
-- Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa
-- Location map module for showing city positions on regional/country maps
-- Modular version - map data stored in separate /data/ submodules
local p = {}
-- Helper function to extract args from frame (reused from Wikidata module)
local function getArgs(frame, ...)
local paramNames = {...}
local args = {}
-- Check if we have any direct parameters
local hasDirectParams = false
for k, v in pairs(frame.args) do
if v and v ~= '' then
hasDirectParams = true
break
end
end
if hasDirectParams then
-- Use frame args, handling empty strings properly
for k, v in pairs(frame.args) do
if v ~= '' then
args[k] = v
end
end
else
-- Use parent args
for k, v in pairs(frame:getParent().args) do
if v ~= '' then
args[k] = v
end
end
end
return args
end
-- Helper function to get map data
local function getMapData(mapName)
if not mapName then
return nil
end
-- Try to load from dati submodule with exact name
local success, mapData = pcall(function()
return require('Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa/dati/' .. mapName)
end)
if success and mapData then
return mapData
end
-- Try with lowercase
success, mapData = pcall(function()
return require('Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa/dati/' .. mw.ustring.lower(mapName))
end)
if success and mapData then
return mapData
end
-- Try with uppercase first letter
success, mapData = pcall(function()
local name = mw.ustring.lower(mapName)
name = mw.ustring.upper(mw.ustring.sub(name, 1, 1)) .. mw.ustring.sub(name, 2)
return require('Mòdulu:Bozza/GianAntonucci/PusizzioniNtâMappa/dati/' .. name)
end)
if success and mapData then
return mapData
end
return nil
end
-- Calculate pixel position from coordinates (FIXED)
local function getPixelPosition(lat, lon, mapData, displayWidth)
local top = mapData.top
local bottom = mapData.bottom
local left = mapData.left
local right = mapData.right
-- Calculate relative position (0 to 1)
local xRatio = (lon - left) / (right - left)
local yRatio = (top - lat) / (top - bottom)
-- Ensure values are within bounds
xRatio = math.max(0, math.min(1, xRatio))
yRatio = math.max(0, math.min(1, yRatio))
-- Calculate display height maintaining aspect ratio
local aspectRatio = mapData.height / mapData.width
local displayHeight = displayWidth * aspectRatio
-- Convert to pixels (FIXED: using correct dimensions)
local x = math.floor(xRatio * displayWidth)
local y = math.floor(yRatio * displayHeight)
return x, y, displayHeight
end
-- Parse coordinates from various formats
local function parseCoordinates(coordStr)
if not coordStr then
return nil, nil
end
-- Remove any leading/trailing whitespace
coordStr = mw.text.trim(coordStr)
-- Try to parse decimal coordinates (e.g., "45.5, 9.2")
local lat, lon = coordStr:match('([%-%.%d]+)%s*,%s*([%-%.%d]+)')
if lat and lon then
return tonumber(lat), tonumber(lon)
end
-- Try to parse DMS format (e.g., "45°30'0"N 9°12'0"E")
local latDeg, latMin, latSec, latDir, lonDeg, lonMin, lonSec, lonDir =
coordStr:match('(%d+)°(%d+)[\'′](%d*)[\"″]?([NS])%s+(%d+)°(%d+)[\'′](%d*)[\"″]?([EW])')
if latDeg then
lat = tonumber(latDeg) + tonumber(latMin)/60 + (tonumber(latSec) or 0)/3600
if latDir == 'S' then lat = -lat end
lon = tonumber(lonDeg) + tonumber(lonMin)/60 + (tonumber(lonSec) or 0)/3600
if lonDir == 'W' then lon = -lon end
return lat, lon
end
return nil, nil
end
-- Get coordinates and region from Wikidata efficiently
local function getWikidataInfo(wikidataId)
local info = {
lat = nil,
lon = nil,
region = nil
}
-- Use the more efficient functions from the Wikidata module if available
local wikidataModule = require('Mòdulu:Bozza/GianAntonucci/Wikidata')
if wikidataModule then
-- Get coordinates
local coordsStr = wikidataModule._getProperty({
property = 'P625',
from = wikidataId,
formatting = 'raw'
})
if coordsStr then
info.lat, info.lon = parseCoordinates(coordsStr)
end
-- Get administrative divisions for map detection
local divisions = wikidataModule._getProperty({
property = 'P131',
from = wikidataId,
formatting = 'label',
separator = '|'
})
if divisions then
-- Check each division to find a matching map
for division in divisions:gmatch('[^|]+') do
division = mw.text.trim(division)
if getMapData(division) then
info.region = division
break
end
end
end
else
-- Fallback to direct Wikibase calls if module not available
local entity = wikidataId and mw.wikibase.getEntity(wikidataId) or mw.wikibase.getEntity()
if entity and entity.claims then
-- Get coordinates
if entity.claims.P625 then
local coords = entity.claims.P625[1].mainsnak.datavalue.value
info.lat = coords.latitude
info.lon = coords.longitude
end
-- Get region
if entity.claims.P131 then
for _, claim in ipairs(entity.claims.P131) do
if claim.mainsnak.datavalue then
local divisionId = 'Q' .. claim.mainsnak.datavalue.value['numeric-id']
local divisionLabel = mw.wikibase.getLabel(divisionId)
if divisionLabel and getMapData(divisionLabel) then
info.region = divisionLabel
break
end
end
end
end
end
end
return info
end
-- Main function to generate the map
function p._main(args)
local mapName = args.map or args[1]
local lat = tonumber(args.lat or args.latitude)
local lon = tonumber(args.lon or args.longitude or args.long)
local fallbackMap = args.fallback or 'Sicilia' -- Changed default fallback to Sicilia
-- Try to parse coordinates if not already numbers
if not lat or not lon then
local coordStr = args.coordinates or args.coord
if coordStr then
lat, lon = parseCoordinates(coordStr)
end
end
-- Get info from Wikidata if needed (single call)
if not lat or not lon or not mapName then
local wikidataId = args.wikidata or args.from
local wikidataInfo = getWikidataInfo(wikidataId)
if not lat then lat = wikidataInfo.lat end
if not lon then lon = wikidataInfo.lon end
if not mapName then mapName = wikidataInfo.region end
end
if not lat or not lon then
return '<div class="error" style="color:red; word-wrap:break-word;">Cuurdinati mancanti</div>'
end
-- DEBUG: Show coordinates and map boundaries
-- return string.format('DEBUG: lat=%s, lon=%s<br>Map: %s<br>Bounds: N=%s, S=%s, W=%s, E=%s',
-- lat, lon, mapName or 'none',
-- mapData.top, mapData.bottom, mapData.left, mapData.right)
-- Get map data
local mapData = getMapData(mapName)
if not mapData then
-- Try fallback if specified
if fallbackMap and fallbackMap ~= 'none' then
mapData = getMapData(fallbackMap)
if mapData then
mapName = fallbackMap
else
-- Fallback also failed - shorter error message
return '<div class="error" style="color:red; word-wrap:break-word;">Mappa nun truvata:<br/>' ..
(mapName or 'nenti') .. ', ' .. fallbackMap .. '</div>'
end
else
-- Shorter error message
return '<div class="error" style="color:red; word-wrap:break-word;">Mappa "' ..
(mapName or '?') .. '" nun truvata</div>'
end
end
-- Build the map HTML
local width = tonumber(args.width) or mapData.width or 280
-- Get label - try provided label, then Wikidata label, then page title
local label = args.label
if not label or label == '' then
local wikidataId = args.wikidata or args.from
if wikidataId then
label = mw.wikibase.getLabel(wikidataId)
end
end
if not label or label == '' then
label = args.name or mw.title.getCurrentTitle().text
end
local labelPos = args.position or args.label_position or 'right'
local markerColor = args.marker_color or args.marker or 'red'
local markerSize = tonumber(args.marker_size) or 8
local showLabel = args.label ~= 'none' and args.show_label ~= 'no'
-- Calculate marker position (with fixed height calculation)
local x, y, displayHeight = getPixelPosition(lat, lon, mapData, width)
-- Build output - return just the image path for the template to handle
-- Check if we're in "simple" mode (just return image name)
if args.simple == 'yes' then
return mapData.image
end
-- Otherwise build full HTML output (for direct module use)
local output = {}
-- Container div with inline-block to contain the image
table.insert(output, '<div class="locmap" style="position:relative; display:inline-block; line-height:0;">')
-- Add the image using HTML img tag (more reliable than wikitext in this context)
table.insert(output, '[[File:' .. mapData.image .. '|' .. width .. 'px|link=]]')
-- Overlay container for marker and label
table.insert(output, '<div style="position:absolute; top:0; left:0; width:' .. width .. 'px; height:' .. displayHeight .. 'px;">')
-- Add marker
local markerOffset = math.floor(markerSize / 2)
table.insert(output, '<div style="position:absolute; left:' .. (x - markerOffset) .. 'px; top:' .. (y - markerOffset) ..
'px; width:' .. markerSize .. 'px; height:' .. markerSize .. 'px; background-color:' .. markerColor ..
'; border:1px solid dark' .. markerColor .. '; border-radius:50%;" title="' .. label .. '"></div>')
-- Add label if requested
if showLabel then
local labelStyle = 'position:absolute; font-size:' .. (args.label_size or '90%') .. '; line-height:110%;'
-- Adjust positioning to be next to the marker, not on top of it
if labelPos == 'left' then
-- Position to the left of the marker
local rightPos = width - x + markerSize + 1
labelStyle = labelStyle .. ' right:' .. math.max(5, rightPos) .. 'px; top:' .. (y - markerOffset) .. 'px; text-align:right;'
elseif labelPos == 'top' then
-- Position above the marker
labelStyle = labelStyle .. ' left:' .. (x - 30) .. 'px; bottom:' .. (displayHeight - y + markerSize + 1) .. 'px; text-align:center; width:60px;'
elseif labelPos == 'bottom' then
-- Position below the marker
labelStyle = labelStyle .. ' left:' .. (x - 30) .. 'px; top:' .. (y + markerSize + 1) .. 'px; text-align:center; width:60px;'
else -- right (default)
-- Position to the right of the marker with minimal spacing
local leftPos = x + markerSize + 1
-- Check if label would go outside bounds
if leftPos + 50 > width then
-- If too close to edge, position to the left instead
labelStyle = labelStyle .. ' right:' .. (width - x + markerSize + 1) .. 'px; top:' .. (y - markerOffset) .. 'px; text-align:right;'
else
labelStyle = labelStyle .. ' left:' .. leftPos .. 'px; top:' .. (y - markerOffset) .. 'px;'
end
end
table.insert(output, '<div style="' .. labelStyle .. '">' .. label .. '</div>')
end
-- Close overlay container
table.insert(output, '</div>')
-- Close main container
table.insert(output, '</div>')
-- Add caption if provided
if args.caption then
table.insert(output, '<div style="text-align:center; font-size:90%; margin-top:5px;">' .. args.caption .. '</div>')
end
return table.concat(output)
end
-- Template entry point
function p.main(frame)
local args = getArgs(frame)
local output = p._main(args)
-- Preprocess the output to ensure wikitext is parsed
return frame:preprocess(output)
end
return p