Diff: Module:Location map
Comparing revision #1 (2023-12-31 15:35:14) with revision #2 (2024-03-04 22:45:29).
| Old | New |
|---|---|
require('strict') |
require('strict') |
local p = {} |
local p = {} |
local getArgs = require('Module:Arguments').getArgs |
local getArgs = require('Module:Arguments').getArgs |
local function round(n, decimals) |
local function round(n, decimals) |
local pow = 10^(decimals or 0) |
local pow = 10^(decimals or 0) |
return math.floor(n * pow + 0.5) / pow |
return math.floor(n * pow + 0.5) / pow |
end |
end |
function p.getMapParams(map, frame) |
function p.getMapParams(map, frame) |
if not map then |
if not map then |
error('The name of the location map definition to use must be specified', 2) |
error('The name of the location map definition to use must be specified', 2) |
end |
end |
local moduletitle = mw.title.new('Module:Location map/data/' .. map) |
local moduletitle = mw.title.new('Module:Location map/data/' .. map) |
if not moduletitle then |
if not moduletitle then |
error(string.format('%q is not a valid name for a location map definition', map), 2) |
error(string.format('%q is not a valid name for a location map definition', map), 2) |
elseif moduletitle.exists then |
elseif moduletitle.exists then |
local mapData = mw.loadData('Module:Location map/data/' .. map) |
local mapData = mw.loadData('Module:Location map/data/' .. map) |
return function(name, params) |
return function(name, params) |
if name == nil then |
if name == nil then |
return 'Module:Location map/data/' .. map |
return 'Module:Location map/data/' .. map |
elseif mapData[name] == nil then |
elseif mapData[name] == nil then |
return '' |
return '' |
elseif params then |
elseif params then |
return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain() |
return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain() |
else |
else |
return mapData[name] |
return mapData[name] |
end |
end |
end |
end |
else |
else |
error('Unable to find the specified location map definition: "Module:Location map/data/' .. map .. '" does not exist', 2) |
error('Unable to find the specified location map definition: "Module:Location map/data/' .. map .. '" does not exist', 2) |
end |
end |
end |
end |
function p.data(frame, args, map) |
function p.data(frame, args, map) |
if not args then |
if not args then |
args = getArgs(frame, {frameOnly = true}) |
args = getArgs(frame, {frameOnly = true}) |
end |
end |
if not map then |
if not map then |
map = p.getMapParams(args[1], frame) |
map = p.getMapParams(args[1], frame) |
end |
end |
local params = {} |
local params = {} |
for k,v in ipairs(args) do |
for k,v in ipairs(args) do |
if k > 2 then |
if k > 2 then |
params[k-2] = v |
params[k-2] = v |
end |
end |
end |
end |
return map(args[2], #params ~= 0 and params) |
return map(args[2], #params ~= 0 and params) |
end |
end |
local hemisphereMultipliers = { |
local hemisphereMultipliers = { |
longitude = { W = -1, w = -1, E = 1, e = 1 }, |
longitude = { W = -1, w = -1, E = 1, e = 1 }, |
latitude = { S = -1, s = -1, N = 1, n = 1 } |
latitude = { S = -1, s = -1, N = 1, n = 1 } |
} |
} |
local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction) |
local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction) |
if decimal then |
if decimal then |
if degrees then |
if degrees then |
error('Decimal and DMS degrees cannot both be provided for ' .. direction, 2) |
error('Decimal and DMS degrees cannot both be provided for ' .. direction, 2) |
elseif minutes then |
elseif minutes then |
error('Minutes can only be provided with DMS degrees for ' .. direction, 2) |
error('Minutes can only be provided with DMS degrees for ' .. direction, 2) |
elseif seconds then |
elseif seconds then |
error('Seconds can only be provided with DMS degrees for ' .. direction, 2) |
error('Seconds can only be provided with DMS degrees for ' .. direction, 2) |
elseif hemisphere then |
elseif hemisphere then |
error('A hemisphere can only be provided with DMS degrees for ' .. direction, 2) |
error('A hemisphere can only be provided with DMS degrees for ' .. direction, 2) |
end |
end |
local retval = tonumber(decimal) |
local retval = tonumber(decimal) |
if retval then |
if retval then |
return retval |
return retval |
end |
end |
error('The value "' .. decimal .. '" provided for ' .. direction .. ' is not valid', 2) |
error('The value "' .. decimal .. '" provided for ' .. direction .. ' is not valid', 2) |
elseif seconds and not minutes then |
elseif seconds and not minutes then |
error('Seconds were provided for ' .. direction .. ' without minutes also being provided', 2) |
error('Seconds were provided for ' .. direction .. ' without minutes also being provided', 2) |
elseif not degrees then |
elseif not degrees then |
if minutes then |
if minutes then |
error('Minutes were provided for ' .. direction .. ' without degrees also being provided', 2) |
error('Minutes were provided for ' .. direction .. ' without degrees also being provided', 2) |
elseif hemisphere then |
elseif hemisphere then |
error('A hemisphere was provided for ' .. direction .. ' without degrees also being provided', 2) |
error('A hemisphere was provided for ' .. direction .. ' without degrees also being provided', 2) |
end |
end |
return nil |
return nil |
end |
end |
decimal = tonumber(degrees) |
decimal = tonumber(degrees) |
if not decimal then |
if not decimal then |
error('The degree value "' .. degrees .. '" provided for ' .. direction .. ' is not valid', 2) |
error('The degree value "' .. degrees .. '" provided for ' .. direction .. ' is not valid', 2) |
elseif minutes and not tonumber(minutes) then |
elseif minutes and not tonumber(minutes) then |
error('The minute value "' .. minutes .. '" provided for ' .. direction .. ' is not valid', 2) |
error('The minute value "' .. minutes .. '" provided for ' .. direction .. ' is not valid', 2) |
elseif seconds and not tonumber(seconds) then |
elseif seconds and not tonumber(seconds) then |
error('The second value "' .. seconds .. '" provided for ' .. direction .. ' is not valid', 2) |
error('The second value "' .. seconds .. '" provided for ' .. direction .. ' is not valid', 2) |
end |
end |
decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600 |
decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600 |
if hemisphere then |
if hemisphere then |
local multiplier = hemisphereMultipliers[direction][hemisphere] |
local multiplier = hemisphereMultipliers[direction][hemisphere] |
if not multiplier then |
if not multiplier then |
error('The hemisphere "' .. hemisphere .. '" provided for ' .. direction .. ' is not valid', 2) |
error('The hemisphere "' .. hemisphere .. '" provided for ' .. direction .. ' is not valid', 2) |
end |
end |
decimal = decimal * multiplier |
decimal = decimal * multiplier |
end |
end |
return decimal |
return decimal |
end |
end |
-- Finds a parameter in a transclusion of {{Coord}}. |
-- Finds a parameter in a transclusion of {{Coord}}. |
local function coord2text(para,coord) -- this should be changed for languages which do not use Arabic numerals or the degree sign |
local function coord2text(para,coord) -- this should be changed for languages which do not use Arabic numerals or the degree sign |
local lat, long = mw.ustring.match(coord,'<span class="p%-latitude latitude">([^<]+)</span><span class="p%-longitude longitude">([^<]+)</span>') |
local lat, long = mw.ustring.match(coord,'<span class="p%-latitude latitude">([^<]+)</span><span class="p%-longitude longitude">([^<]+)</span>') |
if lat then |
if lat then |
return tonumber(para == 'longitude' and long or lat) |
return tonumber(para == 'longitude' and long or lat) |
end |
end |
local result = mw.text.split(mw.ustring.match(coord,'%-?[%.%d]+°[NS] %-?[%.%d]+°[EW]') or '', '[ °]') |
local result = mw.text.split(mw.ustring.match(coord,'%-?[%.%d]+°[NS] %-?[%.%d]+°[EW]') or '', '[ °]') |
if para == 'longitude' then result = {result[3], result[4]} end |
if para == 'longitude' then result = {result[3], result[4]} end |
if not tonumber(result[1]) or not result[2] then |
if not tonumber(result[1]) or not result[2] then |
mw.log('Malformed coordinates value') |
mw.log('Malformed coordinates value') |
mw.logObject(para, 'para') |
mw.logObject(para, 'para') |
mw.logObject(coord, 'coord') |
mw.logObject(coord, 'coord') |
return error('Malformed coordinates value', 2) |
return error('Malformed coordinates value', 2) |
end |
end |
return tonumber(result[1]) * hemisphereMultipliers[para][result[2]] |
return tonumber(result[1]) * hemisphereMultipliers[para][result[2]] |
end |
end |
-- effectively make removeBlanks false for caption and maplink, and true for everything else |
-- effectively make removeBlanks false for caption and maplink, and true for everything else |
-- if useWikidata is present but blank, convert it to false instead of nil |
-- if useWikidata is present but blank, convert it to false instead of nil |
-- p.top, p.bottom, and their callers need to use this |
-- p.top, p.bottom, and their callers need to use this |
function p.valueFunc(key, value) |
function p.valueFunc(key, value) |
if value then |
if value then |
value = mw.text.trim(value) |
value = mw.text.trim(value) |
end |
end |
if value ~= '' or key == 'caption' or key == 'maplink' then |
if value ~= '' or key == 'caption' or key == 'maplink' then |
return value |
return value |
elseif key == 'useWikidata' then |
elseif key == 'useWikidata' then |
return false |
return false |
end |
end |
end |
end |
local function getContainerImage(args, map) |
local function getContainerImage(args, map) |
if args.AlternativeMap then |
if args.AlternativeMap then |
return args.AlternativeMap |
return args.AlternativeMap |
elseif args.relief then |
elseif args.relief then |
local digits = mw.ustring.match(args.relief,'^[1-9][0-9]?$') or '1' -- image1 to image99 |
local digits = mw.ustring.match(args.relief,'^[1-9][0-9]?$') or '1' -- image1 to image99 |
if map('image' .. digits) ~= '' then |
if map('image' .. digits) ~= '' then |
return map('image' .. digits) |
return map('image' .. digits) |
end |
end |
end |
end |
return map('image') |
return map('image') |
end |
end |
function p.top(frame, args, map) |
function p.top(frame, args, map) |
if not args then |
if not args then |
args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc}) |
args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc}) |
end |
end |
if not map then |
if not map then |
map = p.getMapParams(args[1], frame) |
map = p.getMapParams(args[1], frame) |
end |
end |
local width |
local width |
local default_as_number = tonumber(mw.ustring.match(tostring(args.default_width),"%d*")) |
local default_as_number = tonumber(mw.ustring.match(tostring(args.default_width),"%d*")) |
if not args.width then |
if not args.width then |
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1)) |
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1)) |
elseif mw.ustring.sub(args.width, -2) == 'px' then |
elseif mw.ustring.sub(args.width, -2) == 'px' then |
width = mw.ustring.sub(args.width, 1, -3) |
width = mw.ustring.sub(args.width, 1, -3) |
else |
else |
width = args.width |
width = args.width |
end |
end |
local width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0; |
local width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0; |
if width_as_number == 0 then |
if width_as_number == 0 then |
-- check to see if width is junk. If it is, then use default calculation |
-- check to see if width is junk. If it is, then use default calculation |
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1)) |
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1)) |
width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0; |
width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0; |
end |
end |
if args.max_width ~= "" and args.max_width ~= nil then |
if args.max_width ~= "" and args.max_width ~= nil then |
-- check to see if width bigger than max_width |
-- check to see if width bigger than max_width |
local max_as_number = tonumber(mw.ustring.match(args.max_width,"%d*")) or 0; |
local max_as_number = tonumber(mw.ustring.match(args.max_width,"%d*")) or 0; |
if width_as_number>max_as_number and max_as_number>0 then |
if width_as_number>max_as_number and max_as_number>0 then |
width = args.max_width; |
width = args.max_width; |
end |
end |
end |
end |
local retval = frame:extensionTag{name = 'templatestyles', args = {src = 'Module:Location map/styles.css'}} |
local retval = frame:extensionTag{name = 'templatestyles', args = {src = 'Module:Location map/styles.css'}} |
if args.float == 'center' then |
if args.float == 'center' then |
retval = retval .. '<div class="center">' |
retval = retval .. '<div class="center">' |
end |
end |
if args.caption and args.caption ~= '' and args.border ~= 'infobox' then |
if args.caption and args.caption ~= '' and args.border ~= 'infobox' then |
retval = retval .. '<div class="locmap noviewer noresize thumb ' |
retval = retval .. '<div class="locmap noviewer noresize thumb ' |
if args.float == '"left"' or args.float == 'left' then |
if args.float == '"left"' or args.float == 'left' then |
retval = retval .. 'tleft' |
retval = retval .. 'tleft' |
elseif args.float == '"center"' or args.float == 'center' or args.float == '"none"' or args.float == 'none' then |
elseif args.float == '"center"' or args.float == 'center' or args.float == '"none"' or args.float == 'none' then |
retval = retval .. 'tnone' |
retval = retval .. 'tnone' |
else |
else |
retval = retval .. 'tright' |
retval = retval .. 'tright' |
end |
end |
retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px' |
retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px' |
if args.border == 'none' then |
if args.border == 'none' then |
retval = retval .. ';border:none' |
retval = retval .. ';border:none' |
elseif args.border then |
elseif args.border then |
retval = retval .. ';border-color:' .. args.border |
retval = retval .. ';border-color:' .. args.border |
end |
end |
retval = retval .. '"><div style="position:relative;width:' .. width .. 'px' .. (args.border ~= 'none' and ';border:1px solid lightgray">' or '">') |
retval = retval .. '"><div style="position:relative;width:' .. width .. 'px' .. (args.border ~= 'none' and ';border:1px solid lightgray">' or '">') |
else |
else |
retval = retval .. '<div class="locmap" style="width:' .. width .. 'px;' |
retval = retval .. '<div class="locmap" style="width:' .. width .. 'px;' |
if args.float == '"left"' or args.float == 'left' then |
if args.float == '"left"' or args.float == 'left' then |
retval = retval .. 'float:left;clear:left' |
retval = retval .. 'float:left;clear:left' |
elseif args.float == '"center"' or args.float == 'center' then |
elseif args.float == '"center"' or args.float == 'center' then |
retval = retval .. 'float:none;clear:both;margin-left:auto;margin-right:auto' |
retval = retval .. 'float:none;clear:both;margin-left:auto;margin-right:auto' |
elseif args.float == '"none"' or args.float == 'none' then |
elseif args.float == '"none"' or args.float == 'none' then |
retval = retval .. 'float:none;clear:none' |
retval = retval .. 'float:none;clear:none' |
else |
else |
retval = retval .. 'float:right;clear:right' |
retval = retval .. 'float:right;clear:right' |
end |
end |
retval = retval .. '"><div style="width:' .. width .. 'px;padding:0"><div style="position:relative;width:' .. width .. 'px">' |
retval = retval .. '"><div style="width:' .. width .. 'px;padding:0"><div style="position:relative;width:' .. width .. 'px">' |
end |
end |
local image = getContainerImage(args, map) |
local image = getContainerImage(args, map) |
local currentTitle = mw.title.getCurrentTitle() |
local currentTitle = mw.title.getCurrentTitle() |
retval = string.format( |
retval = string.format( |
'%s[[File:%s|%spx|%s%s|class=notpageimage]]', |
'%s[[File:%s|%spx|%s%s|class=notpageimage]]', |
retval, |
retval, |
image, |
image, |
width, |
width, |
args.alt or ((args.label or currentTitle.text) .. ' is located in ' .. map('name')), |
args.alt or ((args.label or currentTitle.text) .. ' is located in ' .. map('name')), |
args.maplink and ('|link=' .. args.maplink) or '' |
args.maplink and ('|link=' .. args.maplink) or '' |
) |
) |
if args.caption and args.caption ~= '' then |
if args.caption and args.caption ~= '' then |
if (currentTitle.namespace == 0) and mw.ustring.find(args.caption, '##') then |
if (currentTitle.namespace == 0) and mw.ustring.find(args.caption, '##') then |
retval = retval .. '[[Category:Pages using location map with a double number sign in the caption]]' |
retval = retval .. '[[Category:Pages using location map with a double number sign in the caption]]' |
end |
end |
end |
end |
if args.overlay_image then |
if args.overlay_image then |
return retval .. '<div style="position:absolute;top:0;left:0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|class=notpageimage]]</div>' |
return retval .. '<div style="position:absolute;top:0;left:0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|class=notpageimage]]</div>' |
else |
else |
return retval |
return retval |
end |
end |
end |
end |
function p.bottom(frame, args, map) |
function p.bottom(frame, args, map) |
if not args then |
if not args then |
args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc}) |
args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc}) |
end |
end |
if not map then |
if not map then |
map = p.getMapParams(args[1], frame) |
map = p.getMapParams(args[1], frame) |
end |
end |
local retval = '</div>' |
local retval = '</div>' |
local currentTitle = mw.title.getCurrentTitle() |
local currentTitle = mw.title.getCurrentTitle() |
if not args.caption or args.border == 'infobox' then |
if not args.caption or args.border == 'infobox' then |
if args.border then |
if args.border then |
retval = retval .. '<div style="padding-top:0.2em">' |
retval = retval .. '<div style="padding-top:0.2em">' |
else |
else |
retval = retval .. '<div style="font-size:91%;padding-top:3px">' |
retval = retval .. '<div style="font-size:91%;padding-top:3px">' |
end |
end |
retval = retval |
retval = retval |
.. (args.caption or (args.label or currentTitle.text) .. ' (' .. map('name') .. ')') |
.. (args.caption or (args.label or currentTitle.text) .. ' (' .. map('name') .. ')') |
.. '</div>' |
.. '</div>' |
elseif args.caption ~= '' then |
elseif args.caption ~= '' then |
-- This is not the pipe trick. We're creating a link with no text on purpose, so that CSS can give us a nice image |
-- This is not the pipe trick. We're creating a link with no text on purpose, so that CSS can give us a nice image |
retval = retval .. '<div class="thumbcaption"><div class="magnify">[[:File:' .. getContainerImage(args, map) .. '|class=notpageimage| ]]</div>' .. args.caption .. '</div>' |
retval = retval .. '<div class="thumbcaption"><div class="magnify">[[:File:' .. getContainerImage(args, map) .. '|class=notpageimage| ]]</div>' .. args.caption .. '</div>' |
end |
end |
if args.switcherLabel then |
if args.switcherLabel then |
retval = retval .. '<span class="switcher-label" style="display:none">' .. args.switcherLabel .. '</span>' |
retval = retval .. '<span class="switcher-label" style="display:none">' .. args.switcherLabel .. '</span>' |
elseif args.autoSwitcherLabel then |
elseif args.autoSwitcherLabel then |
retval = retval .. '<span class="switcher-label" style="display:none">Show map of ' .. map('name') .. '</span>' |
retval = retval .. '<span class="switcher-label" style="display:none">Show map of ' .. map('name') .. '</span>' |
end |
end |
retval = retval .. '</div></div>' |
retval = retval .. '</div></div>' |
if args.caption_undefined then |
if args.caption_undefined then |
mw.log('Removed parameter caption_undefined used.') |
mw.log('Removed parameter caption_undefined used.') |
local parent = frame:getParent() |
local parent = frame:getParent() |
if parent then |
if parent then |
mw.log('Parent is ' .. parent:getTitle()) |
mw.log('Parent is ' .. parent:getTitle()) |
end |
end |
mw.logObject(args, 'args') |
mw.logObject(args, 'args') |
if currentTitle.namespace == 0 then |
if currentTitle.namespace == 0 then |
retval = retval .. '[[Category:Location maps with removed parameters|caption_undefined]]' |
retval = retval .. '[[Category:Location maps with removed parameters|caption_undefined]]' |
end |
end |
end |
end |
if map('skew') ~= '' or map('lat_skew') ~= '' or map('crosses180') ~= '' or map('type') ~= '' then |
if map('skew') ~= '' or map('lat_skew') ~= '' or map('crosses180') ~= '' or map('type') ~= '' then |
mw.log('Removed parameter used in map definition ' .. map()) |
mw.log('Removed parameter used in map definition ' .. map()) |
if currentTitle.namespace == 0 then |
if currentTitle.namespace == 0 then |
local key = (map('skew') ~= '' and 'skew' or '') .. |
local key = (map('skew') ~= '' and 'skew' or '') .. |
(map('lat_skew') ~= '' and 'lat_skew' or '') .. |
(map('lat_skew') ~= '' and 'lat_skew' or '') .. |
(map('crosses180') ~= '' and 'crosses180' or '') .. |
(map('crosses180') ~= '' and 'crosses180' or '') .. |
(map('type') ~= '' and 'type' or '') |
(map('type') ~= '' and 'type' or '') |
retval = retval .. '[[Category:Location maps with removed parameters|' .. key .. ' ]]' |
retval = retval .. '[[Category:Location maps with removed parameters|' .. key .. ' ]]' |
end |
end |
end |
end |
if string.find(map('name'), '|', 1, true) then |
if string.find(map('name'), '|', 1, true) then |
mw.log('Pipe used in name of map definition ' .. map()) |
mw.log('Pipe used in name of map definition ' .. map()) |
if currentTitle.namespace == 0 then |
if currentTitle.namespace == 0 then |
retval = retval .. '[[Category:Location maps with a name containing a pipe]]' |
retval = retval .. '[[Category:Location maps with a name containing a pipe]]' |
end |
end |
end |
end |
if args.float == 'center' then |
if args.float == 'center' then |
retval = retval .. '</div>' |
retval = retval .. '</div>' |
end |
end |
return retval |
return retval |
end |
end |
local function markOuterDiv(x, y, imageDiv, labelDiv) |
local function markOuterDiv(x, y, imageDiv, labelDiv) |
return mw.html.create('div') |
return mw.html.create('div') |
:addClass('od') |
:addClass('od') |
:addClass('notheme') -- T236137 |
:addClass('notheme') -- T236137 |
:cssText('top:' .. round(y, 3) .. '%;left:' .. round(x, 3) .. '%') |
:cssText('top:' .. round(y, 3) .. '%;left:' .. round(x, 3) .. '%') |
:node(imageDiv) |
:node(imageDiv) |
:node(labelDiv) |
:node(labelDiv) |
end |
end |
local function markImageDiv(mark, marksize, label, link, alt, title) |
local function markImageDiv(mark, marksize, label, link, alt, title) |
local builder = mw.html.create('div') |
local builder = mw.html.create('div') |
:addClass('id') |
:addClass('id') |
:cssText('left:-' .. round(marksize / 2) .. 'px;top:-' .. round(marksize / 2) .. 'px') |
:cssText('left:-' .. round(marksize / 2) .. 'px;top:-' .. round(marksize / 2) .. 'px') |
:attr('title', title) |
:attr('title', title) |
if marksize ~= 0 then |
if marksize ~= 0 then |
builder:wikitext(string.format( |
builder:wikitext(string.format( |
'[[File:%s|%dx%dpx|%s|link=%s%s|class=notpageimage]]', |
'[[File:%s|%dx%dpx|%s|link=%s%s|class=notpageimage]]', |
mark, |
mark, |
marksize, |
marksize, |
marksize, |
marksize, |
label, |
label, |
link, |
link, |
alt and ('|alt=' .. alt) or '' |
alt and ('|alt=' .. alt) or '' |
)) |
)) |
end |
end |
return builder |
return builder |
end |
end |
local function markLabelDiv(label, label_size, label_width, position, background, x, marksize) |
local function markLabelDiv(label, label_size, label_width, position, background, x, marksize) |
if tonumber(label_size) == 0 then |
if tonumber(label_size) == 0 then |
return mw.html.create('div'):addClass('l0'):wikitext(label) |
return mw.html.create('div'):addClass('l0'):wikitext(label) |
end |
end |
local builder = mw.html.create('div') |
local builder = mw.html.create('div') |
:cssText('font-size:' .. label_size .. '%;width:' .. label_width .. 'em') |
:cssText('font-size:' .. label_size .. '%;width:' .. label_width .. 'em') |
local distance = round(marksize / 2 + 1) |
local distance = round(marksize / 2 + 1) |
if position == 'top' then -- specified top |
if position == 'top' then -- specified top |
builder:addClass('pv'):cssText('bottom:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em') |
builder:addClass('pv'):cssText('bottom:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em') |
elseif position == 'bottom' then -- specified bottom |
elseif position == 'bottom' then -- specified bottom |
builder:addClass('pv'):cssText('top:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em') |
builder:addClass('pv'):cssText('top:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em') |
elseif position == 'left' or (tonumber(x) > 70 and position ~= 'right') then -- specified left or autodetected to left |
elseif position == 'left' or (tonumber(x) > 70 and position ~= 'right') then -- specified left or autodetected to left |
builder:addClass('pl'):cssText('right:' .. distance .. 'px') |
builder:addClass('pl'):cssText('right:' .. distance .. 'px') |
else -- specified right or autodetected to right |
else -- specified right or autodetected to right |
builder:addClass('pr'):cssText('left:' .. distance .. 'px') |
builder:addClass('pr'):cssText('left:' .. distance .. 'px') |
end |
end |
builder = builder:tag('div') |
builder = builder:tag('div') |
:wikitext(label) |
:wikitext(label) |
if background then |
if background then |
builder:cssText('background-color:' .. background) |
builder:cssText('background-color:' .. background) |
end |
end |
return builder:done() |
return builder:done() |
end |
end |
local function getX(longitude, left, right) |
local function getX(longitude, left, right) |
local width = (right - left) % 360 |
local width = (right - left) % 360 |
if width == 0 then |
if width == 0 then |
width = 360 |
width = 360 |
end |
end |
local distanceFromLeft = (longitude - left) % 360 |
local distanceFromLeft = (longitude - left) % 360 |
-- the distance needed past the map to the right equals distanceFromLeft - width. the distance needed past the map to the left equals 360 - distanceFromLeft. to minimize page stretching, go whichever way is shorter |
-- the distance needed past the map to the right equals distanceFromLeft - width. the distance needed past the map to the left equals 360 - distanceFromLeft. to minimize page stretching, go whichever way is shorter |
if distanceFromLeft - width / 2 >= 180 then |
if distanceFromLeft - width / 2 >= 180 then |
distanceFromLeft = distanceFromLeft - 360 |
distanceFromLeft = distanceFromLeft - 360 |
end |
end |
return 100 * distanceFromLeft / width |
return 100 * distanceFromLeft / width |
end |
end |
local function getY(latitude, top, bottom) |
local function getY(latitude, top, bottom) |
return 100 * (top - latitude) / (top - bottom) |
return 100 * (top - latitude) / (top - bottom) |
end |
end |
function p.mark(frame, args, map) |
function p.mark(frame, args, map) |
if not args then |
if not args then |
args = getArgs(frame, {wrappers = 'Template:Location map~'}) |
args = getArgs(frame, {wrappers = 'Template:Location map~'}) |
end |
end |
local mapnames = {} |
local mapnames = {} |
if not map then |
if not map then |
if args[1] then |
if args[1] then |
map = {} |
map = {} |
for mapname in mw.text.gsplit(args[1], '#', true) do |
for mapname in mw.text.gsplit(args[1], '#', true) do |
map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame) |
map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame) |
mapnames[#mapnames + 1] = mapname |
mapnames[#mapnames + 1] = mapname |
end |
end |
if #map == 1 then map = map[1] end |
if #map == 1 then map = map[1] end |
else |
else |
map = p.getMapParams('World', frame) |
map = p.getMapParams('World', frame) |
args[1] = 'World' |
args[1] = 'World' |
end |
end |
end |
end |
if type(map) == 'table' then |
if type(map) == 'table' then |
local outputs = {} |
local outputs = {} |
local oldargs = args[1] |
local oldargs = args[1] |
for k,v in ipairs(map) do |
for k,v in ipairs(map) do |
args[1] = mapnames[k] |
args[1] = mapnames[k] |
outputs[k] = tostring(p.mark(frame, args, v)) |
outputs[k] = tostring(p.mark(frame, args, v)) |
end |
end |
args[1] = oldargs |
args[1] = oldargs |
return table.concat(outputs, '#PlaceList#') .. '#PlaceList#' |
return table.concat(outputs, '#PlaceList#') .. '#PlaceList#' |
end |
end |
local x, y, longitude, latitude |
local x, y, longitude, latitude |
longitude = decdeg(args.lon_deg, args.lon_min, args.lon_sec, args.lon_dir, args.long, 'longitude') |
longitude = decdeg(args.lon_deg, args.lon_min, args.lon_sec, args.lon_dir, args.long, 'longitude') |
latitude = decdeg(args.lat_deg, args.lat_min, args.lat_sec, args.lat_dir, args.lat, 'latitude') |
latitude = decdeg(args.lat_deg, args.lat_min, args.lat_sec, args.lat_dir, args.lat, 'latitude') |
if args.excludefrom then |
if args.excludefrom then |
-- If this mark is to be excluded from certain maps entirely (useful in the context of multiple maps) |
-- If this mark is to be excluded from certain maps entirely (useful in the context of multiple maps) |
for exclusionmap in mw.text.gsplit(args.excludefrom, '#', true) do |
for exclusionmap in mw.text.gsplit(args.excludefrom, '#', true) do |
-- Check if this map is excluded. If so, return an empty string. |
-- Check if this map is excluded. If so, return an empty string. |
if args[1] == exclusionmap then |
if args[1] == exclusionmap then |
return '' |
return '' |
end |
end |
end |
end |
end |
end |
local builder = mw.html.create() |
local builder = mw.html.create() |
local currentTitle = mw.title.getCurrentTitle() |
local currentTitle = mw.title.getCurrentTitle() |
if args.coordinates then |
if args.coordinates then |
-- Temporarily removed to facilitate infobox conversion. See [[Wikipedia:Coordinates in infoboxes]] |
-- Temporarily removed to facilitate infobox conversion. See [[Wikipedia:Coordinates in infoboxes]] |
-- if longitude or latitude then |
-- if longitude or latitude then |
-- error('Coordinates from [[Module:Coordinates]] and individual coordinates cannot both be provided') |
-- error('Coordinates from [[Module:Coordinates]] and individual coordinates cannot both be provided') |
-- end |
-- end |
longitude = coord2text('longitude', args.coordinates) |
longitude = coord2text('longitude', args.coordinates) |
latitude = coord2text('latitude', args.coordinates) |
latitude = coord2text('latitude', args.coordinates) |
elseif not longitude and not latitude and args.useWikidata then |
elseif not longitude and not latitude and args.useWikidata then |
-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't. |
-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't. |
local entity = mw.wikibase.getEntity() |
local entity = mw.wikibase.getEntity() |
if entity and entity.claims and entity.claims.P625 and entity.claims.P625[1].mainsnak.snaktype == 'value' then |
if entity and entity.claims and entity.claims.P625 and entity.claims.P625[1].mainsnak.snaktype == 'value' then |
local value = entity.claims.P625[1].mainsnak.datavalue.value |
local value = entity.claims.P625[1].mainsnak.datavalue.value |
longitude, latitude = value.longitude, value.latitude |
longitude, latitude = value.longitude, value.latitude |
end |
end |
if args.link and (currentTitle.namespace == 0) then |
if args.link and (currentTitle.namespace == 0) then |
builder:wikitext('[[Category:Location maps with linked markers with coordinates from Wikidata]]') |
builder:wikitext('[[Category:Location maps with linked markers with coordinates from Wikidata]]') |
end |
end |
end |
end |
if not longitude then |
if not longitude then |
error('No value was provided for longitude') |
error('No value was provided for longitude') |
elseif not latitude then |
elseif not latitude then |
error('No value was provided for latitude') |
error('No value was provided for latitude') |
end |
end |
if currentTitle.namespace > 0 then |
if currentTitle.namespace > 0 then |
if (not args.lon_deg) ~= (not args.lat_deg) then |
if (not args.lon_deg) ~= (not args.lat_deg) then |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Degrees]]') |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Degrees]]') |
elseif (not args.lon_min) ~= (not args.lat_min) then |
elseif (not args.lon_min) ~= (not args.lat_min) then |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Minutes]]') |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Minutes]]') |
elseif (not args.lon_sec) ~= (not args.lat_sec) then |
elseif (not args.lon_sec) ~= (not args.lat_sec) then |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Seconds]]') |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Seconds]]') |
elseif (not args.lon_dir) ~= (not args.lat_dir) then |
elseif (not args.lon_dir) ~= (not args.lat_dir) then |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Hemisphere]]') |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Hemisphere]]') |
elseif (not args.long) ~= (not args.lat) then |
elseif (not args.long) ~= (not args.lat) then |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Decimal]]') |
builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Decimal]]') |
end |
end |
end |
end |
if ((tonumber(args.lat_deg) or 0) < 0) and ((tonumber(args.lat_min) or 0) ~= 0 or (tonumber(args.lat_sec) or 0) ~= 0 or (args.lat_dir and args.lat_dir ~='')) then |
if ((tonumber(args.lat_deg) or 0) < 0) and ((tonumber(args.lat_min) or 0) ~= 0 or (tonumber(args.lat_sec) or 0) ~= 0 or (args.lat_dir and args.lat_dir ~='')) then |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
end |
end |
if ((tonumber(args.lon_deg) or 0) < 0) and ((tonumber(args.lon_min) or 0) ~= 0 or (tonumber(args.lon_sec) or 0) ~= 0 or (args.lon_dir and args.lon_dir ~= '')) then |
if ((tonumber(args.lon_deg) or 0) < 0) and ((tonumber(args.lon_min) or 0) ~= 0 or (tonumber(args.lon_sec) or 0) ~= 0 or (args.lon_dir and args.lon_dir ~= '')) then |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
end |
end |
if (((tonumber(args.lat_min) or 0) < 0) or ((tonumber(args.lat_sec) or 0) < 0)) then |
if (((tonumber(args.lat_min) or 0) < 0) or ((tonumber(args.lat_sec) or 0) < 0)) then |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
end |
end |
if (((tonumber(args.lon_min) or 0) < 0) or ((tonumber(args.lon_sec) or 0) < 0)) then |
if (((tonumber(args.lon_min) or 0) < 0) or ((tonumber(args.lon_sec) or 0) < 0)) then |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]') |
end |
end |
if args.skew or args.lon_shift or args.markhigh then |
if args.skew or args.lon_shift or args.markhigh then |
mw.log('Removed parameter used in invocation.') |
mw.log('Removed parameter used in invocation.') |
local parent = frame:getParent() |
local parent = frame:getParent() |
if parent then |
if parent then |
mw.log('Parent is ' .. parent:getTitle()) |
mw.log('Parent is ' .. parent:getTitle()) |
end |
end |
mw.logObject(args, 'args') |
mw.logObject(args, 'args') |
if currentTitle.namespace == 0 then |
if currentTitle.namespace == 0 then |
local key = (args.skew and 'skew' or '') .. |
local key = (args.skew and 'skew' or '') .. |
(args.lon_shift and 'lon_shift' or '') .. |
(args.lon_shift and 'lon_shift' or '') .. |
(args.markhigh and 'markhigh' or '') |
(args.markhigh and 'markhigh' or '') |
builder:wikitext('[[Category:Location maps with removed parameters|' .. key ..' ]]') |
builder:wikitext('[[Category:Location maps with removed parameters|' .. key ..' ]]') |
end |
end |
end |
end |
if map('x') ~= '' then |
if map('x') ~= '' then |
x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude }))) |
x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude }))) |
else |
else |
x = tonumber(getX(longitude, map('left'), map('right'))) |
x = tonumber(getX(longitude, map('left'), map('right'))) |
end |
end |
if map('y') ~= '' then |
if map('y') ~= '' then |
y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude }))) |
y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude }))) |
else |
else |
y = tonumber(getY(latitude, map('top'), map('bottom'))) |
y = tonumber(getY(latitude, map('top'), map('bottom'))) |
end |
end |
if (x < 0 or x > 100 or y < 0 or y > 100) and not args.outside then |
if (x < 0 or x > 100 or y < 0 or y > 100) and not args.outside then |
mw.log('Mark placed outside map boundaries without outside flag set. x = ' .. x .. ', y = ' .. y) |
mw.log('Mark placed outside map boundaries without outside flag set. x = ' .. x .. ', y = ' .. y) |
local parent = frame:getParent() |
local parent = frame:getParent() |
if parent then |
if parent then |
mw.log('Parent is ' .. parent:getTitle()) |
mw.log('Parent is ' .. parent:getTitle()) |
end |
end |
mw.logObject(args, 'args') |
mw.logObject(args, 'args') |
if currentTitle.namespace == 0 then |
if currentTitle.namespace == 0 then |
local key = currentTitle.prefixedText |
local key = currentTitle.prefixedText |
builder:wikitext('[[Category:Location maps with marks outside map and outside parameter not set|' .. key .. ' ]]') |
builder:wikitext('[[Category:Location maps with marks outside map and outside parameter not set|' .. key .. ' ]]') |
end |
end |
end |
end |
local mark = args.mark or map('mark') |
local mark = args.mark or map('mark') |
if mark == '' then |
if mark == '' then |
mark = 'Red pog.svg' |
mark = 'Red pog.svg' |
end |
end |
local marksize = tonumber(args.marksize) or tonumber(map('marksize')) or 8 |
local marksize = tonumber(args.marksize) or tonumber(map('marksize')) or 8 |
local imageDiv = markImageDiv(mark, marksize, args.label or mw.title.getCurrentTitle().text, args.link or '', args.alt, args[2]) |
local imageDiv = markImageDiv(mark, marksize, args.label or mw.title.getCurrentTitle().text, args.link or '', args.alt, args[2]) |
local labelDiv |
local labelDiv |
if args.label and args.position ~= 'none' then |
if args.label and args.position ~= 'none' then |
labelDiv = markLabelDiv(args.label, args.label_size or 91, args.label_width or 6, args.position, args.background, x, marksize) |
labelDiv = markLabelDiv(args.label, args.label_size or 91, args.label_width or 6, args.position, args.background, x, marksize) |
end |
end |
return builder:node(markOuterDiv(x, y, imageDiv, labelDiv)) |
return builder:node(markOuterDiv(x, y, imageDiv, labelDiv)) |
end |
end |
local function switcherSeparate(s) |
local function switcherSeparate(s) |
if s == nil then return {} end |
if s == nil then return {} end |
local retval = {} |
local retval = {} |
for i in string.gmatch(s .. '#', '([^#]*)#') do |
for i in string.gmatch(s .. '#', '([^#]*)#') do |
i = mw.text.trim(i) |
i = mw.text.trim(i) |
retval[#retval + 1] = (i ~= '' and i) |
retval[#retval + 1] = (i ~= '' and i) |
end |
end |
return retval |
return retval |
end |
end |
function p.main(frame, args, map) |
function p.main(frame, args, map) |
local caption_list = {} |
local caption_list = {} |
if not args then |
if not args then |
args = getArgs(frame, {wrappers = 'Template:Location map', valueFunc = p.valueFunc}) |
args = getArgs(frame, {wrappers = 'Template:Location map', valueFunc = p.valueFunc}) |
end |
end |
if args.useWikidata == nil then |
if args.useWikidata == nil then |
args.useWikidata = true |
args.useWikidata = true |
end |
end |
if not map then |
if not map then |
if args[1] then |
if args[1] then |
map = {} |
map = {} |
for mapname in string.gmatch(args[1], '[^#]+') do |
for mapname in string.gmatch(args[1], '[^#]+') do |
map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame) |
map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame) |
end |
end |
if args['caption'] then |
if args['caption'] then |
if args['caption'] == "" then |
if args['caption'] == "" then |
while #caption_list < #map do |
while #caption_list < #map do |
caption_list[#caption_list + 1] = args['caption'] |
caption_list[#caption_list + 1] = args['caption'] |
end |
end |
else |
else |
for caption in mw.text.gsplit(args['caption'], '##', true) do |
for caption in mw.text.gsplit(args['caption'], '##', true) do |
caption_list[#caption_list + 1] = caption |
caption_list[#caption_list + 1] = caption |
end |
end |
end |
end |
end |
end |
if #map == 1 then map = map[1] end |
if #map == 1 then map = map[1] end |
else |
else |
map = p.getMapParams('World', frame) |
map = p.getMapParams('World', frame) |
end |
end |
end |
end |
if type(map) == 'table' then |
if type(map) == 'table' then |
local altmaps = switcherSeparate(args.AlternativeMap) |
local altmaps = switcherSeparate(args.AlternativeMap) |
if #altmaps > #map then |
if #altmaps > #map then |
error(string.format('%d AlternativeMaps were provided, but only %d maps were provided', #altmaps, #map)) |
error(string.format('%d AlternativeMaps were provided, but only %d maps were provided', #altmaps, #map)) |
end |
end |
local overlays = switcherSeparate(args.overlay_image) |
local overlays = switcherSeparate(args.overlay_image) |
if #overlays > #map then |
if #overlays > #map then |
error(string.format('%d overlay_images were provided, but only %d maps were provided', #overlays, #map)) |
error(string.format('%d overlay_images were provided, but only %d maps were provided', #overlays, #map)) |
end |
end |
if #caption_list > #map then |
if #caption_list > #map then |
error(string.format('%d captions were provided, but only %d maps were provided', #caption_list, #map)) |
error(string.format('%d captions were provided, but only %d maps were provided', #caption_list, #map)) |
end |
end |
local outputs = {} |
local outputs = {} |
args.autoSwitcherLabel = true |
args.autoSwitcherLabel = true |
for k,v in ipairs(map) do |
for k,v in ipairs(map) do |
args.AlternativeMap = altmaps[k] |
args.AlternativeMap = altmaps[k] |
args.overlay_image = overlays[k] |
args.overlay_image = overlays[k] |
args.caption = caption_list[k] |
args.caption = caption_list[k] |
outputs[k] = p.main(frame, args, v) |
outputs[k] = p.main(frame, args, v) |
end |
end |
return '<div class="switcher-container">' .. table.concat(outputs) .. '</div>' |
return '<div class="switcher-container">' .. table.concat(outputs) .. '</div>' |
else |
else |
return p.top(frame, args, map) .. tostring( p.mark(frame, args, map) ) .. p.bottom(frame, args, map) |
return p.top(frame, args, map) .. tostring( p.mark(frame, args, map) ) .. p.bottom(frame, args, map) |
end |
end |
end |
end |
return p |
return p |