Modul:Location map
Zur Navigation springen
Zur Suche springen
Die Dokumentation für dieses Modul kann unter Modul:Location map/Doku erstellt werden
-- documentation
local LocationMap = {
suite = 'Location map',
serial = '2022-10-21',
item = 15934920
}
-- module import
-- require( 'strict' )
local cd = require( 'Module:Coordinates' )
local li = require( 'Module:Location map/i18n' )
local lp = require( 'Module:Location map/Params' )
-- module variable
local locMap = {
maintenance = {}
}
-- Local functions, please do not call them directly
local function split( s )
local tb = mw.text.split( s, ';' )
for i = #tb, 1, -1 do
tb[ i ] = mw.text.trim( tb[ i ] )
if tb[ i ] == '' then
table.remove( tb, i )
end
end
return tb
end
local function getClass( style, list )
local tb = split( style )
local class, rule
for i = #tb, 1, -1 do
rule = tb[ i ]
if list[ rule ] then
class = list[ rule ]
table.remove( tb, i )
end
end
return class, table.concat( tb, '; ' )
end
local function addMaintenance( s )
if s and s ~= '' then
table.insert( locMap.maintenance, s )
end
end
local function getMaintenance()
local m = table.concat( locMap.maintenance, ' ' )
if m ~= '' then
m = '<span class="error">' .. m .. '</span>'
end
return m
end
local function isSet( s )
if not s then
return s
end
s = mw.text.trim( s )
if s ~= '' then
return s
else
return nil
end
end
local function round( n )
return math.floor( n * 100 + 0.5 ) / 100
end
local function setLocation( args )
local lmarksize = math.floor( args.marksize + 0.5 )
local msize = math.floor( ( args.marksize - 1 ) / 2 + 0.5 )
local msize3 = math.floor( ( args.marksize + 2 ) / 2 + 0.5 )
local msize5 = math.floor( ( args.marksize + 4 ) / 2 + 0.5 )
local centerPosition = -msize .. 'px'
-- create marker box
local markerBox = mw.html.create( 'div' )
:addClass( 'voy-locmap-marker-box' )
:css( { top = round( args.y * 100 ) .. '%',
left = round( args.x * 100 ) .. '%' } )
-- add marker symbol
if args.mark ~= 'none' then
markerBox:node( mw.html.create( 'div' )
:addClass( 'voy-locmap-marker-symbol' )
:css( {
top = centerPosition,
left = centerPosition,
[ 'min-width' ] = lmarksize .. 'px',
[ 'min-height' ] = lmarksize .. 'px'
} )
:wikitext( mw.ustring.format( '[[File:%s|%sx%spx|top|class=noviewer notpageimage|link=%s|%s]]',
args.mark, lmarksize, lmarksize, args.name, args.name) )
)
end
-- add label
local wrapClass, labelClass
if args.label ~= '' and args.label ~= 'none' then
if args.labelWrap == 'manual' then
wrapClass = 'voy-locmap-marker-label-nowrap'
end
local styles = {}
local pos = li.labelPositions[ args.labelPosition ]
if pos then
pos = pos:gsub( 'msize_', msize )
:gsub( 'msize3_', msize3 )
:gsub( 'msize5_', msize5 )
table.insert( styles, pos )
else
-- estimation of posititon
if args.y <= 0.5 then
table.insert( styles, li.labelPositions.yTop:format( msize3 ) )
else
table.insert( styles, li.labelPositions.yBottom:format( msize3 ) )
end
if args.x < 0.25 then
table.insert( styles,
li.labelPositions.xLeft:format( math.floor( 3 - 60 * args.x ) / 10 ) )
elseif args.x <= 0.75 then
table.insert( styles, li.labelPositions.xCenter )
else
table.insert( styles,
li.labelPositions.xRight:format( math.floor( 3 - 60 * ( 1 - args.x ) ) / 10 ) )
end
end
labelClass, args.labelStyle = getClass( args.labelStyle, li.labelClasses )
table.insert( styles, args.labelStyle )
markerBox:node( mw.html.create( 'div' )
:addClass( 'voy-locmap-marker-label' )
:addClass( wrapClass )
:addClass( labelClass )
:cssText( isSet( table.concat( styles, ' ' ) ) )
:node( mw.html.create( 'span' )
:addClass( 'voy-locmap-marker-label-text' )
:wikitext( args.label )
)
)
end
return tostring( markerBox )
end
local function baseMap( args )
-- map and map container
local map = mw.ustring.format( '[[File:%s|%spx|center|class=noviewer notpageimage|link=|%s]]',
args.mapImage, args.width, args.description )
-- add marker
if args.x < 0 or args.x > 1 or args.y < 0 or args.y > 1 then
map = map .. tostring( mw.html.create( 'div' )
:addClass( 'voy-locmap-error' )
:wikitext( mw.ustring.format( li.errMsgs.coordError, args.name ) )
)
else
map = map .. setLocation( args )
end
local style = ( args.caption ~= '' and args.captionInnerBorder ~= '' )
and ( 'border:' .. args.captionInnerBorder ) or ''
map = mw.html.create( 'div' )
:addClass( 'voy-locmap-map-box' )
:cssText( isSet( style ) )
:wikitext( map .. args.places ) -- adding places to map
-- add map caption
local caption, class
if args.caption ~= '' then
caption = mw.html.create( 'div' )
:addClass( 'thumbcaption voy-locmap-caption' )
:cssText( isSet( args.captionStyle ) )
:wikitext( args.caption )
end
-- create outer box
class, style = getClass( args.mapStyle, li.mapClasses )
class = isSet( class ) or 'voy-locmap-center'
if args.caption ~= '' and args.captionOuterBorder ~= '' then
style = style .. '; border:' .. args.captionOuterBorder
end
return tostring( mw.html.create( 'div' )
:addClass( 'voy-locmap' )
:addClass( class )
:addClass( args.caption ~= '' and 'voy-locmap-with-caption' or nil )
:cssText( isSet( style ) )
:node( map )
:node( caption )
)
end
-- Handling regional map data
-- This function is never to be called directly but with a pcall()
-- to handle exceptions in case of missing map modules
local function getMapData( id )
local region = require( li.modulePrefix .. id )
if region then
region.id = id
end
return region
end
local function linearX( mapData, long )
local left = mapData.left
local right = mapData.right
if not mapData or not left or not right or left == right then
return -1 -- error
elseif left < right then
return ( long - left ) / ( right - left )
elseif long < 0 then
return ( 360 + long - left ) / ( 360 + right - left )
else
return ( long - left ) / ( 360 + right - left )
end
end
local function linearY( mapData, lat )
local top = mapData.top
local bottom = mapData.bottom
if not mapData or not top or not bottom or top == bottom then
return -1 -- error
end
return ( lat - top ) / ( bottom - top )
end
local function getX( mapData, long, lat )
if mapData.x then
return mapData.x( lat, long )
else
return linearX( mapData, long )
end
end
local function getY( mapData, long, lat )
if mapData.y then
return mapData.y( lat, long )
else
return linearY( mapData, lat )
end
end
local function getMapImage( mapData, which )
local image = mapData.default
if which == 'quickbar' then
which = li.defaults.quickbarMapType
if ( mapData.quickbar or '' ) ~= '' then
which = mapData.quickbar
end
end
if which ~= '' and ( mapData[ which ] or '' ) ~= '' then
image = mapData[ which ]
end
return image
end
-- parameters handling
local function argCheck( param, altValue )
if not param or param == '' then
return altValue
end
param = mw.text.trim( param )
if param == '' then
param = altValue
end
return param
end
-- checking a set of arguments
local function argsCheck( args, keys )
for i, key in ipairs( keys ) do
args[ key ] = argCheck( args[ key ], '' )
end
end
local function checkMarkerProperties( args, mapData )
args.mark = argCheck( args.mark, mapData.mark or li.defaults.markerImg )
args.marksize = argCheck( args.marksize, mapData.marksize or li.defaults.markerSize )
argsCheck( args, { 'name', 'label', 'labelWrap', 'labelPosition', 'labelStyle',
'labelBackground' } )
if args.labelBackground ~= '' then
args.labelBackground = 'background: ' .. args.labelBackground
if args.labelStyle ~= '' then
args.labelStyle = args.labelStyle .. '; ' .. args.labelBackground
else
args.labelStyle = args.labelBackground
end
end
return args
end
local function checkCoordinate( args, mapData )
local success = true
local t
args.lat = argCheck( tostring( args.lat ), '' )
args.long = argCheck( tostring( args.long ), '' )
if args.lat ~= '' and args.long ~= '' then
t = tonumber( args.lat )
if t then
args.lat = math.abs( t ) <= 90 and t or ''
else
t = cd.toDec( args.lat, 'lat', 6 )
args.lat = t.error == 0 and t.dec or ''
end
t = tonumber( args.long )
if t then
args.long = ( t > -180 and t <= 180 ) and t or ''
else
t = cd.toDec( args.long, 'long', 6 )
args.long = t.error == 0 and t.dec or ''
end
end
if args.lat == '' or args.long == '' then
return -1, -1, false
end
local x = getX( mapData, args.long, args.lat )
if x < 0 or x > 1 then
success = false
if x == -1 then
addMaintenance( li.errMsgs.wrongXBorders )
else
addMaintenance( mw.ustring.format( li.errMsgs.wrongLong,
tonumber( args.long ) or 0 ) )
end
end
local y = getY( mapData, args.long, args.lat )
if y < 0 or y > 1 then
success = false
if y == -1 then
addMaintenance( li.errMsgs.wrongYBorders )
else
addMaintenance( mw.ustring.format( li.errMsgs.wrongLat,
tonumber( args.lat ) or 0 ) )
end
end
return x, y, success
end
local function checkParameters( args, list )
local unknown = {}
for key, value in pairs( args ) do
if not list[ key ] then
table.insert( unknown, "''" .. key .. "''" )
end
end
local category = li.errMsgs.wrongParam
if #unknown == 1 then
addMaintenance( category
.. mw.ustring.format( li.errMsgs.unknownParam, unknown[ 1 ] ) )
elseif #unknown > 1 then
addMaintenance( category .. mw.ustring.format( li.errMsgs.unknownParams,
table.concat( unknown, ', ' ) ) )
end
end
-- Map functions
local function apiLocationMap( args )
local map = argCheck( args.map, 'missing' )
local success, mapData = pcall( getMapData, map )
if not success then
return mw.ustring.format( li.errMsgs.unknownMap, map )
end
-- Parameters check
addMaintenance( checkParameters( args, lp.locationMap ) )
if not args.lat or not args.long then
addMaintenance( li.errMsgs.notANumber )
return getMaintenance()
end
args.x, args.y, success = checkCoordinate( args, mapData )
args.maptype = argCheck( args.maptype, 'default' )
args.mapImage = argCheck( args.alternativeMap, getMapImage( mapData, args.maptype ) )
if ( args.mapImage or '' ) == '' then
success = false
addMaintenance( li.errMsgs.noMapImage )
end
if not success then
return getMaintenance()
end
argsCheck( args, { 'caption', 'captionStyle', 'captionInnerBorder',
'captionOuterBorder', 'places', 'mapStyle' } )
-- Image size and description
args.width = argCheck( tostring( args.width ), '' )
if not args.width:match( '^%d+$' ) and not args.width:match( '^%d*x%d+$' ) then
args.width = li.defaults.mapSize
end
args.description = mapData.description or ''
args = checkMarkerProperties( args, mapData )
return baseMap( args ) .. getMaintenance()
end
local function apiAddLocation( args )
local map = argCheck( args.map, 'missing' )
local success, mapData = pcall( getMapData, map )
if not success then
return mw.ustring.format( li.errMsgs.unknownMap, map )
end
-- Parameters check
addMaintenance( checkParameters( args, lp.locationMapLocation ) )
if not args.lat or not args.long then
addMaintenance( li.errMsgs.notANumber )
return getMaintenance()
end
args.x, args.y, success = checkCoordinate( args, mapData )
if not success then
return getMaintenance()
end
args = checkMarkerProperties( args, mapData )
return setLocation( args ) .. getMaintenance()
end
local function apiAddObject( args )
argsCheck( args, { 'object', 'right', 'left', 'top', 'bottom', 'objectStyle',
'objectBackground' } )
if args.object == '' then
return li.errMsgs.noObject
end
local success = true
addMaintenance( checkParameters( args, lp.locationMapObject ) )
if args.right == '' and args.left == '' then
success = false
addMaintenance( li.errMsgs.noXPos )
end
if args.top == '' and args.bottom == '' then
success = false
addMaintenance( li.errMsgs.noYPos )
end
if not success then
return getMaintenance()
end
if args.objectBackground ~='' then
args.objectBackground = 'background: ' .. args.objectBackground
if args.objectStyle ~='' then
args.objectStyle = args.objectStyle .. '; ' .. args.objectBackground
else
args.objectStyle = args.objectBackground
end
end
local style, labelClass
if args.left ~= '' then
style = 'left: ' .. args.left .. ';'
else
style = 'right: ' .. args.right .. ';'
end
if args.top ~= '' then
style = style .. 'top: ' .. args.top .. ';'
else
style = style .. 'bottom: ' .. args.bottom .. ';'
end
labelClass, args.objectStyle = getClass( args.objectStyle, li.labelClasses )
style = style .. args.objectStyle
return tostring( mw.html.create( 'div' )
:addClass( 'voy-locmap-object' )
:addClass( labelClass )
:cssText( isSet( style ) )
:wikitext( args.object )
) .. getMaintenance()
end
-- Documentation of map data
local function apiGetMapValue( args )
local map = argCheck( args.map, 'missing' )
local success, mapData = pcall( getMapData, map )
if not success then
return mw.ustring.format( li.errMsgs.unknownMap, map )
end
args.param = argCheck( args.param, '' )
if args.param == '' then
return li.errMsgs.noParam
else
return mapData[ args.param ] or li.errMsgs.anError
end
end
local function apiGetMapValueSet( args )
local map = argCheck( args.map, 'missing' )
local success, mapData = pcall( getMapData, map )
if not success then
return mw.ustring.format( li.errMsgs.unknownMap, map )
end
local row, v
local list = mw.html.create( 'table' )
:addClass( li.mapDocs.tableClass )
for i, j in ipairs( li.paramList ) do
v = mapData[ j ]
if not v then
v = li.errMsgs.notDefined
else
if j == 'default' or j == 'relief' then
v = mw.ustring.format( '[[c:File:%s|%s]]', v, v )
elseif li.mapDocs[ v ] then
v = li.mapDocs[ v ]
end
end
row = mw.html.create( 'tr' )
:node(
mw.html.create( 'th' )
:css( 'text-align', 'left' )
:wikitext( li.mapDocs[ j ] )
)
:node(
mw.html.create( 'td' )
:wikitext( v )
)
list:node( row )
end
local titleObj = mw.title.getCurrentTitle()
if titleObj.text == titleObj.baseText then
-- not a subpage
if not mapData.relief then
addMaintenance( li.errMsgs.noReliefMap )
end
end
return tostring( list ) .. getMaintenance()
end
-- API function calls
local function templateStyles()
local frame = mw.getCurrentFrame()
return frame:extensionTag( 'templatestyles', '', { src = 'Module:Location map/styles.css' } );
end
locMap[ li.api.apiLocationMap ] = function( frame )
return templateStyles() .. apiLocationMap( frame.args )
end
locMap[ li.api.apiAddLocation ] = function( frame )
return apiAddLocation( frame.args )
end
locMap[ li.api.apiAddObject ] = function( frame )
return apiAddObject( frame.args )
end
locMap[ li.api.apiGetMapValue ] = function( frame )
return apiGetMapValue( frame.args )
end
locMap[ li.api.apiGetMapValueSet ] = function( frame )
return apiGetMapValueSet(frame.args)
end
-- example for usage in a Lua script
function locMap.exampleLuaCall()
local frame = {}
frame.args = {
map = 'de',
lat = 52.51789,
long = 13.38873,
name = 'Berlin',
label = '[[Berlin]]',
}
return locMap.locationMap( frame )
end
return locMap