Module:Advanced map
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Advanced map/doc
-- <nowiki>
local maps = require('Module:Map')
local hc = require('Module:Paramtest').has_content
local icons
local p = {}
-- For color incrementing
local curcolor = 1
local keycolor = 1
local dotcolors = {'#ffff00', '#01ff7f', '#ff0101', '#ff7f01', '#ff017f', '#c0407f', '#ff01fc', '#a040c0',
'#7f01ff', '#4040c0', '#017fff', '#40a0c0', '#01fffc', '#40c07f', '#61c040', '#84ff01', '#c05e40', '#c0be40', '#808080', '#ffffff'}
-- Possible style args
local styles = { 'title', 'description', 'label', 'class', 'stroke', 'stroke-opacity', 'stroke-width', 'fill', 'fill-opacity', 'radius', 'r', 'icon', 'iconWikiLink', 'direction' }
local pairstyles = { 'iconSize', 'iconAnchor', 'popupAnchor' }
-- Pin types
local pinTypes = {
['greyPin'] = 'pin_grey.svg',
['redPin'] = 'pin_red.svg',
['greenPin'] = 'pin_green.svg',
['bluePin'] = 'pin_blue.svg',
['cyanPin'] = 'pin_cyan.svg',
['magentaPin'] = 'pin_magenta.svg',
['yellowPin'] = 'pin_yellow.svg',
}
-- Create JSON
function toJSON(j)
local json_good, json = pcall(mw.text.jsonEncode, j)--, mw.text.JSON_PRETTY)
if json_good then
return json
end
return error('Error converting to JSON')
end
-- Parse coordinates
function parseCoords(str)
local ret = {}
for u in mw.text.gsplit(str, '%s*;%s*') do
local xy = mw.text.split(u, '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x and y then
table.insert(ret, {x=x, y=y})
end
end
if #ret < 1 then error('Error the following is not a valid coordinate set: '..str) end
return ret
end
function parseComplCoords(str)
local ret,i = {},1
for v in mw.text.gsplit(str, '%s*;%s*;%s*') do
ret[i] = {}
for u in mw.text.gsplit(v, '%s*;%s*') do
local xy = mw.text.split(u, '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x and y then
table.insert(ret[i], {x, y})
end
end
if #ret[i] < 1 then error('Error the following is not a valid coordinate set: '..str) end
i = i + 1
end
if #ret < 1 then error('Error the following is not a valid coordinate set: '..str) end
return ret
end
-- Single point type features
function pointFeats(ftargs, opts, coords, typ)
local ret = {}
-- Force a new color if none given
if typ ~= 'pin' and typ ~= 'circle' and typ ~= 'icon' and typ ~= 'text' then
if not ftargs.fill then
ftargs.fill = dotcolors[curcolor]
curcolor = curcolor + 1
if curcolor > #dotcolors then
curcolor = 1
end
end
end
local mult = false
if #coords > 1 then
mult = true
end
for _,v in ipairs(coords) do
v.desc = ftargs.desc or ''
local args = ftargs
if mult then
args = mw.clone(ftargs)
end
if typ == 'circle' then
-- Circle
local cargs = args
cargs.x = v.x
cargs.y = v.y
table.insert(ret, maps.featCircle(cargs, opts))
elseif typ == 'text' then
-- Text label
table.insert(ret, maps.featText(args, opts, v))
elseif typ == 'pin' then
-- Pin
table.insert(ret, maps.featPin(args, opts, v))
elseif typ == 'icon' then
-- Pin but use iconWikiLink with a predefine one
args.icon = string.lower(args.icon)
table.insert(ret, maps.featIcon(args, opts, v))
elseif typ == 'square' or typ == 'sq' then
-- Square dot
table.insert(ret, maps.featSqDot(args, opts, v))
else
-- Dot is the default
table.insert(ret, maps.featDot(args, opts, v))
end
end
return ret
end
-- Generate the map key
function genKey(args)
local ret = mw.html.create('ul')
-- Look for up to 125 feature groups, defined in order
for i=1, 125 do
if args['key'..i] or args['coords'..i] and hc(args['coords'..i]) then
local typ = string.lower(args['type'..i] or 'dot')
local show = false
if args['key'..i] and args['title'..i] then
show = true
elseif typ ~= 'text' and args['coords'..i] and hc(args['coords'..i]) then
show = true
end
if args['nokey'..i] then
show = false
end
if show then
local val = args['title'..i] or args['label'..i] or 'Missing title'
local autocolor
if typ ~= 'pin' and typ ~= 'circle' and typ ~= 'icon' and typ ~= 'polygon' and typ ~= 'complex-polygon' and typ ~= 'complpoly' and typ ~= 'line' and typ ~= 'lines' and typ ~= 'text' and typ ~= 'key' then
if not args['fill'..i] then
autocolor = dotcolors[keycolor]
keycolor = keycolor + 1
if keycolor > #dotcolors then
keycolor = 1
end
end
end
local symb = mw.html.create('div')
symb:addClass('amap-key-symb')
if typ == 'key' then
-- Only title text, no symbol
symb:addClass('amap-key-nosymb')
elseif typ == 'text' then
local cl = 'leaflet-vis-tooltip'
if args['class'..i] then
cl = cl .. ' ' .. args['class'..i]
end
symb:addClass('map-style-preview')
:tag('div'):addClass(cl):wikitext('XX'):done()
elseif typ == 'polygon' or typ == 'circle' or typ == 'complex-polygon' or typ == 'complpoly' then
local cl = 'amap-key-poly'
if typ == 'circle' then cl = 'amap-key-circ' end
local brdCol = args['stroke'..i] or '#3388ff'
local brdWi = args['stroke-width'..i] or 3
symb:tag('div'):addClass(cl):css({
border = brdWi..'px solid '..brdCol,
opacity = args['stroke-opacity'..i] or 1
})
:tag('div'):css({
background = args['fill'..i] or '#3388ff',
opacity = args['fill-opacity'..i] or 0.2
}):done():done()
elseif typ == 'line' or typ == 'lines' then
local brdCol = args['stroke'..i] or '#3388ff'
local brdWi = tonumber(args['stroke-width'..i]) or 3
brdWi = brdWi / 2 -- div height 0 so border renders double height
symb:tag('div'):addClass('amap-key-line'):css({
border = brdWi..'px solid '..brdCol,
opacity = args['stroke-opacity'..i] or 1
}):done()
elseif typ == 'pin' then
local iconSize = '26x42px'
local pimg
if args['iconWikiLink'..i] then
pimg = args['iconWikiLink'..i]
else
pimg = args['icon'..i] or 'greenPin'
if pinTypes[pimg] then
pimg = pinTypes[pimg]
else
pimg = 'pin_green.svg'
end
end
if args['iconSize'..i] then
iconSize = string.gsub(args['iconSize'..i], '%s*,%s*', 'x')..'px'
end
symb:wikitext( string.format('[[File:%s|%s|link=]]', pimg, iconSize) )
elseif typ == 'icon' then
if not icons then
icons = mw.loadData('Module:Map/icons')
end
local ic = string.lower(args['icon'..i])
ic = icons[ic]
if not ic then error('Invalid icon name, see [[Module:Map/icons]] for available icons and aliases') end
symb:wikitext( string.format('[[File:%s|%sx%spx|link=]]', ic.icon, math.ceil(ic.iconSize[1]), math.ceil(ic.iconSize[2])) )
else
-- Dot or square dot
local cl = 'leaflet-dot'
if typ == 'square' or typ == 'sq' then cl = 'leaflet-sqdot' end
symb:tag('div'):addClass('amap-key-dots')
:tag('div'):addClass(cl):css({ background = autocolor or args['fill'..i] or '#3388ff'})
:done()
:done()
end
ret:tag('li')
:node(symb)
:tag('div')
:addClass('amap-key-text')
:wikitext(val)
:done()
:done()
end
else
break
end
end
if args.compass then
ret:tag('li')
:tag('div'):addClass('amap-key-symb amap-key-nosymb'):done()
:tag('div')
:addClass('amap-key-text amap-key-compass')
:wikitext('[[File:Compass.png|35x35px|link=]]')
:done()
:done()
end
return tostring(ret)
end
-- Category args
function catArg(arg)
local ret = ''
for u in mw.text.gsplit(arg, '%s*,%s*') do
if u and u ~= '' then
ret = ret..string.format('[[Category:%s]]', u)
end
end
return ret
end
-- Edit buttons
function navBar(name)
local viewSpan = mw.html.create( 'span' )
:attr( 'title', 'View this map' )
:wikitext( 'v' )
local talkSpan = mw.html.create( 'span' )
:attr( 'title', 'Discussion about this map' )
:wikitext( 'd' )
local editSpan = mw.html.create( 'span' )
:attr( 'title', 'You can edit this map. Please use the preview button before saving.' )
:wikitext( 'e' )
local pgtitle = mw.title.getCurrentTitle()
local ret = mw.html.create('div')
:addClass('amap-nav plainlinks noprint')
if pgtitle.prefixedText ~= 'Map:'..name then
ret :wikitext( '[[Map:'..name..'|'..tostring(viewSpan)..']]' )
:wikitext( ' ' )
:tag( 'span' ):css( 'font-size', '80%' ):wikitext( '•' ):done()
:wikitext( ' ' )
end
ret :wikitext( '['..tostring( mw.uri.fullUrl( 'Map talk:'..name ) )..' '..tostring(talkSpan)..']' )
:wikitext( ' ' )
:tag( 'span' ):css( 'font-size', '80%' ):wikitext( '•' ):done()
:wikitext( ' ' )
:wikitext( '['..tostring( mw.uri.fullUrl( 'Map:'..name, 'action=edit' ) )..' '..tostring(editSpan)..']' )
return ret
end
function p.main(frame)
local args = frame:getParent().args
-- Combination parameters
if args.center and hc(args.center) then
local xy = mw.text.split(args.center, '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x then args.x = x end
if y then args.y = y end
end
if args.dimensions then
local xy = mw.text.split(args.dimensions, '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x then args.width = x end
if y then args.height = y end
elseif args.size then
local xy = mw.text.split(args.size, '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x then args.width = x end
if y then args.height = y end
end
-- for norpreprocess just return mapframe
if args.nopreprocess then
return p.genMap(args)
elseif args.maponly then
return p.genMap(args)
end
local title = args.title or 'Add a title!'
local width = args.width or 600
local key = genKey(args)
local intmap = p.genMap(args)
local class = 'advanced-map'
local nsp = mw.title.getCurrentTitle().namespace
-- translate "centre" spelling for align, set alignment, all centered in map namespace
if nsp ~= 118 and args.align then
local align = string.lower(args.align)
if align == 'left' then
class = class..' amap-left'
elseif align == 'right' then
class = class..' amap-right'
--elseif align == 'centre' or align == 'center' then
end
end
-- optional drop shadows for pins/imgs
if args.shadows then
local shadow = string.lower(args.shadows)
if shadow == 'dark' then
class = class..' amap-dropdark'
elseif shadow == 'light' then
class = class..' amap-droplight'
end
end
local ret = mw.html.create('div')
ret :addClass(class)
:css({ width = width..'px' })
-- Add navigaion buttons to top left like navbox
if args.name then
ret:node( navBar(args.name) )
end
if string.find(title, '%[%[') then
ret:tag('div'):addClass('amap-title')
:wikitext(title)
:done()
else
ret:tag('div'):addClass('amap-title')
:tag('span'):wikitext(title):done()
:done()
end
if args.keyonly then
ret:tag('div')
:addClass('amap-key')
:wikitext(key)
:done()
elseif args.nokey then
ret:tag('div')
:addClass('amap-map')
:wikitext(intmap)
:done()
else
ret:tag('div')
:addClass('amap-map')
:wikitext(intmap)
:done()
:tag('div')
:addClass('amap-key')
:wikitext(key)
:done()
end
ret = tostring(ret)
if nsp == 0 then
-- On main
ret = ret .. '[[Category:Pages using advanced maps]]'
if args.pgcats then
ret = ret .. catArg(args.pgcats)
end
elseif nsp == 118 then
-- Map namespace
ret = ret .. '[[Category:Advanced maps]]'
if args.mapcats then
ret = ret .. catArg(args.mapcats)
end
end
return ret
end
function p.genMap(args)
-- Shared options
local opts = {
mapID = args.mapID or 28, -- RuneScape Surface
plane = tonumber(args.plane) or 0,
}
local featCol = {}
-- Look for up to 125 feature groups, defined in order
for i=1, 125 do
if args['coords'..i] and hc(args['coords'..i]) then
local typ = string.lower(args['type'..i] or 'dot')
local coords = parseCoords( args['coords'..i] )
local ftargs = {}
-- Complex coords
local complcoords
if typ == 'complex-polygon' or typ == 'complpoly' or typ == 'lines' then
complcoords = parseComplCoords( args['coords'..i] )
end
-- Get styles for map feature group
for _,s in ipairs(styles) do
if args[s..i] and hc(args[s..i]) then
ftargs[s] = args[s..i]
end
end
if ftargs.radius then
ftargs.r = ftargs.radius
end
for _,s in ipairs(pairstyles) do
if args[s..i] and hc(args[s..i]) then
local xy = mw.text.split(args[s..i], '%s*,%s*')
local x = tonumber(xy[1])
local y = tonumber(xy[2])
if x and y then
ftargs[s] = {x, y}
end
end
end
if typ == 'complex-polygon' or typ == 'complpoly' then
if #complcoords > 1 then
table.insert(featCol, maps.featComplPolygon(ftargs, opts, complcoords))
else
ftargs.pins = complcoords[1]
table.insert(featCol, maps.featPolygon(ftargs, opts))
end
elseif typ == 'lines' then
for _,v in ipairs(complcoords) do
local lcoords = {}
for _,k in ipairs(v) do
table.insert(lcoords, {x=k[1], y=k[2]})
end
ftargs.pins = lcoords
table.insert(featCol, maps.featLine(ftargs, opts))
end
elseif typ == 'polygon' then
ftargs.pins = coords
table.insert(featCol, maps.featPolygon(ftargs, opts))
elseif typ == 'line' then
ftargs.pins = coords
table.insert(featCol, maps.featLine(ftargs, opts))
else
-- Single point features
local subfts = pointFeats(ftargs, opts, coords, typ)
for _,f in ipairs(subfts) do
table.insert(featCol, f)
end
end
elseif args['key'..i] and hc(args['key'..i]) then
else
break
end
end
local ftjson = {
type = 'FeatureCollection',
features = featCol
}
local mapopts = {
x = args.x,
y = args.y,
width = args.width or 600,
height = args.height or 600,
mapID = opts.mapID,
plane = opts.plane,
zoom = args.zoom or 2,
align = 'center',
plainTiles = 'true'
}
if hc(args.group) then
mapopts.group = args.group
end
if args.maponly and args.align then
mapopts.align = args.align
end
-- translate "centre" spelling for align
if mapopts.align == 'centre' then
mapopts.align = 'center'
end
if hc(args.caption) then
mapopts.text = args.caption
else
mapopts.frameless = ''
end
if hc(args.showicons) then
mapopts.plainTiles = 'false'
end
if hc(args.mapVersion) then
mapopts.mapVersion = args.mapVersion
end
-- mw.logObject(ftjson)
-- Create map element
local mapelem = mw.html.create('mapframe')
mapelem:attr(mapopts):newline():wikitext(toJSON(ftjson)):newline()
if args.nopreprocess then
return tostring(mapelem)
end
return mw.getCurrentFrame():preprocess(tostring(mapelem))
end
return p