Modul:Location map

Aus Wikivoyage
Zur Navigation springen Zur Suche springen
Template-info.png Dokumentation für das Modul Location map[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Das Modul Location map stellt Funktionen zur Erstellung von Positionskarten bereit.

Funktionen

Lagekarte von Ägypten
Golf von
Suez
Ägypten
Lagekarte der Welt
Kairo
Kairo
Orte in Ägypten
Karte Russlands

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

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:

Hinweise
-- documentation
local LocationMap = {
	suite  = 'Location map',
	serial = '2020-06-14',
	item   = 15934920
}

-- module import
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 analyzeStyle( style, list )
	local tb = split( style )
	local l
	for i, tbItem in ipairs( tb ) do
		l = list[ tbItem ]
		if l then
			tb[ i ] = l
		end
	end
	return 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 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' )
		:cssText( li.styles.location:gsub( ';$', '' ) )
		:css( { top = args.y * 100 .. '%',
			left = args.x * 100 .. '%' } )

	-- add marker symbol
	if args.mark ~= 'none' then
		markerBox:node( mw.html.create( 'div' )
			:cssText( li.styles.location:gsub( ';$', '' ) )
			:css( {
				top = centerPosition,
				left = centerPosition,
				[ 'min-width' ] = lmarksize .. 'px',
				[ 'min-height' ] = lmarksize .. 'px'
			} )
			:wikitext( mw.ustring.format( '[[File:%s|%sx%spx|top|class=noviewer|link=%s|%s]]',
				args.mark, lmarksize, lmarksize, args.name, args.name) )
		)
	end

	-- add label
	if args.label ~= '' and args.label ~= 'none' then
		local styles = { li.styles.location }
		if args.labelWrap and args.labelWrap == 'manual' then
			table.insert( styles, li.styles.labelWidthNowrap )
		else
			-- prevent early wrapping
			table.insert( styles, li.styles.labelWidth )
		end
		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, 'top:' .. msize3 .. 'px;' )
			else
				table.insert( styles, 'bottom:' .. msize3 .. 'px;' )
			end
			if args.x < 0.25 then
				table.insert( styles, 'text-align:left; left:' 
					.. math.floor( 3 - 60 * args.x ) / 10 .. 'em;' )
			elseif args.x < 0.75 then
				table.insert( styles, li.styles.centerX )
			else
				table.insert( styles, 'text-align:right; right:'
					.. math.floor( 10 * ( 0.3 - ( 1 - args.x ) * 6 ) ) / 10 .. 'em;' )
			end
		end
		table.insert( styles, analyzeStyle( args.labelStyle, li.labelStyles ) )

		markerBox:node( mw.html.create( 'div' )
			:cssText( table.concat( styles, ' ' ) )
			: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|%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' )
			:cssText( li.styles.mapError )
			:wikitext( mw.ustring.format( li.errMsgs.coordError, args.name ) )
		)
	else
		map = map .. setLocation( args )
	end

	-- add places
	map = map .. args.places
	local style = li.styles.mapDiv .. ( args.caption == '' and 'border:none'
		or ( 'border:' .. args.captionInnerBorder ) )
	map = mw.html.create( 'div' )
		:cssText( style )
		:wikitext( map )

	-- create outer box
	style = li.styles.mapBox
	if args.caption ~= '' then
		style = style .. 'border:' .. args.captionOuterBorder .. ';'
	end
	style = style .. analyzeStyle( args.mapStyle, li.mapStyles )

	local mapBox = mw.html.create( 'table' )
		:addClass( 'locationMap' )
		:cssText( style )
	local row = mw.html.create( 'tr' )
		:node( mw.html.create( 'td' )
			:addClass( 'thumb' )
			:cssText( li.styles.mapCell
				.. ( args.caption == '' and 'padding:0' or '' ) )
			:node( map )
		)
	mapBox:node( row )

	-- add map caption
	if args.caption ~= '' then
		row = mw.html.create( 'tr' )
			:node( mw.html.create( 'td' )
				:addClass( 'thumbcaption' )
				:cssText( li.styles.mapCaption .. args.captionStyle )
				:wikitext( args.caption )
			)
		mapBox:node( row )
	end

	return tostring( mapBox )
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
		return region
	end
	return nil
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
		-- error
		return -1
	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
		-- error
		return -1
	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 and mapData.quickbar ~= '' then
			which = mapData.quickbar
		end
	end
	if which ~= '' and mapData[ which ] and mapData[ which ] ~= '' then
		image = mapData[ which ]
	end
	return image
end

-- parameters handling
local function argCheck( param, altValue )
	if not param or param == '' then
		return altValue
	end
	local val = mw.text.trim( param )
	if val == '' then
		val = altValue
	end
	return val
end

local function checkMarkerProperties( args, mapData )
	args.name = argCheck( args.name, '' )
	args.label = argCheck( args.label, '' )

	args.mark = argCheck( args.mark, mapData.mark or li.defaults.marker )
	args.marksize = argCheck( args.marksize, mapData.marksize or li.defaults.marksize )
	args.labelStyle = argCheck( args.labelStyle, '' )
	args.labelBackground = argCheck( args.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
	args.labelWrap = argCheck( args.labelWrap, 'auto' )
	args.labelPosition = argCheck( args.labelPosition, 'auto' )
			
	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 not args.mapImage or args.mapImage == '' then
		success = false
		addMaintenance( li.errMsgs.noMapImage )
	end
	if not success then
		return getMaintenance()
	end

	args.caption = argCheck( args.caption, '' )
	args.captionStyle = argCheck( args.captionStyle, '' )
	args.captionInnerBorder = argCheck( args.captionInnerBorder, li.styles.innerBorder )
	args.captionOuterBorder = argCheck( args.captionOuterBorder, li.styles.outerBorder )
	args.places = argCheck( args.places, '' )
	args.mapStyle = argCheck( args.mapStyle, 'center' )

	-- 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.imgSize
	end
	args.description = ''
	if mapData.description then
		args.description = mapData.description
	end

	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 )
	args.object = argCheck( args.object, '' )
	if args.object == '' then
		return li.errMsgs.noObject
	end
	
	local success = true
	addMaintenance( checkParameters( args, lp.locationMapObject ) )

	args.right = argCheck( args.right, '' )
	args.left = argCheck( args.left, '' )
	if args.right == '' and args.left == '' then
		success = false
		addMaintenance( li.errMsgs.noXPos )
	end
	args.top = argCheck( args.top, '' )
	args.bottom = argCheck( args.bottom, '' )
	if args.top == '' and args.bottom == '' then
		success = false
		addMaintenance( li.errMsgs.noYPos )
	end
	if not success then
		return getMaintenance()
	end
		
	args.objectStyle = argCheck( args.objectStyle, '' )
	args.objectBackground = argCheck( args.objectBackground, '' )
	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 = li.styles.objectBox
	if args.left ~= '' then
		style = style .. 'left:' .. args.left .. ';'
	else
		style = style .. 'right:' .. args.right .. ';'
	end
	if args.top ~= '' then
		style = style .. 'top: ' .. args.top .. ';'
	else
		style = style .. 'bottom: ' .. args.bottom .. ';'
	end
	style = style .. analyzeStyle( args.objectStyle, li.labelStyles )
	return tostring( mw.html.create( 'div' )
		:addClass( 'locationMapObject' )
		:cssText( 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
			sCode = v
		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

locMap[ li.api.apiLocationMap ] = function( frame )
	return 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