Modul:Location map
Dieses Modul ist getestet und für den projektweiten Gebrauch geeignet. Es kann in Vorlagen benutzt und auf Hilfeseiten erläutert werden. Entwicklungen an dem Modul sollten auf Location map/Test und die Anwendung auf der Spielwiese getestet werden, da wiederholte Trial-and-Error-Edits die Resourcen stark belasten können. |
Dieses Modul verwendet TemplateStyles. Die Stilvorlagen sind in den folgenden Dateien notiert: |
Das Modul Location map
stellt Funktionen zur Erstellung von Positionskarten bereit. Technische Hintergrundinformationen befinden sich in Wikivoyage:Location map.
Funktionen
Kartenfunktionen
function locMap.locationMap(frame)
Stellt eine Karte mit einem Ort dar. Es können weitere Orte und Objekte eingefügt werden.
function locMap.addLocation(frame)
Ergänzt einen weiteren Ort auf der Karte.
function locMap.addObject(frame)
Fügt ein beliebiges Objekt in die Karte ein.
Dokumentation regionaler Kartendaten
function locMap.getMapValue(frame)
Gibt den Wert eines Parameters einer Kartendefinition aus.
function locMap.getMapValueSet(frame)
Erstellt eine Tabelle mit den Parametern einer Kartendefinition.
Benötigte weitere Module
Dieses Modul benötigt folgende weitere Module: Coordinates • Location map/i18n • Location map/Params • Location map/styles.css
Verwendung in anderen Modulen
Dieses Modul ist notwendig für die Ausführung folgender Module. Bei Anpassungen sollte die Funktionstüchtigkeit der folgenden Module geprüft werden. Benutze dazu auch diese Tracking-Kategorie um Fehler zu finden, die sich dann auf Artikel auswirken:
- Quickbar
- Modul benötigt das Modul Location map – Wartungskategorie, in der nochmals alle Module gelistet sind, die von diesem Modul abhängig sind.
- Die obige Dokumentation wurde aus der Seite Modul:Location map/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
-- documentation
local LocationMap = {
suite = 'Location map',
serial = '2024-07-06',
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 .. '; color: inherit'
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