Modul:Kartposisjon

Moduldokumentasjon

Denne modulen benyttes av kartmalene {{Kartposisjon}}, {{Kartposisjon+}}, {{Kartposisjon~}} og {{Kartposisjon mange}}. Se dokumentasjonssidene til disse malene for bruk og instruksjoner.

require('strict')

local p = {}

local getArgs = require('Module:Arguments').getArgs
local Pgname = require('Modul:String').replace

local function round(n, decimals)
	local pow = 10^(decimals or 0)
	return math.floor(n * pow + 0.5) / pow
end

function p.getMapParams(map, frame)
	if not map then
		error('Navnet på kartdefinisjonen som skal brukes må angis', 2)
	end
	local moduletitle = mw.title.new('Modul:Kartposisjon/data/' .. map)
	if not moduletitle then
		error('"' .. map .. '" er ikke et gyldig navn for en kartdefinisjon', 2)
	elseif moduletitle.exists then
		local mapData = mw.loadData('Modul:Kartposisjon/data/' .. map)
		return function(name, params)
			if name == nil then
				return 'Modul:Kartposisjon/data/' .. map
			elseif mapData[name] == nil then
				return ''
			elseif params then
				return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain()
			else
				return mapData[name]
			end
		end
	elseif mw.title.new('Mal:Kartposisjon ' .. map).exists then
		local cache = {}
		if type(frame) ~= 'table' or type(frame.expandTemplate) ~= 'function' then
			error('En ramme må oppgis ved bruk av et eldre positionskart')
		end
		return function(name, params)
			if params then
				return frame:expandTemplate{title = 'Kartposisjon ' .. map, args = { name, unpack(params) }}
			else
				if name == nil then
					return 'Mal:Kartposisjon ' .. map
				elseif cache[name] == nil then
					cache[name] = frame:expandTemplate{title = 'Kartposisjon ' .. map, args = { name }}
				end
				return cache[name]
			end
		end
	else
		error('Kunne ikke finne angitt kartdefinisjon. Verken "Modul:Kartposisjon/data/' .. map .. '" eller "Mal:Kartposisjon ' .. map .. '" finnes', 2)
	end
end

function p.data(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local params = {}
	for k,v in ipairs(args) do
		if k > 2 then
			params[k-2] = v
		end
	end
	return map(args[2], #params ~= 0 and params)
end

local hemisphereMultipliers = {
	longitude = { W = -1, w = -1, E = 1, e = 1 },
	latitude = { S = -1, s = -1, N = 1, n = 1 }
}

local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction)
	if decimal then
		if degrees then
			error('Desimal og DMS-grader kan ikke begge angis for ' .. direction, 2)
		elseif minutes then
			error('Minutter kan kun angis som ' .. direction .. ' sammen med DMS-grader', 2)
		elseif seconds then
			error('Sekunder kan kun angis som ' .. direction .. ' sammen med DMS-grader', 2)
		elseif hemisphere then
			error('En halvkule kan kun angis som ' .. direction .. ' sammen med DMS-grader', 2)
		end
		local retval = tonumber(decimal)
		if retval then
			return retval
		end
		error('Verdien "' .. decimal .. '" angitt for ' .. direction .. ' er ugyldig', 2)
	elseif seconds and not minutes then
		error('Sekunder ble angitt for ' .. direction .. ' uten at minutes også ble angitt', 2)
	elseif not degrees then
		if minutes then
			error('Minutter ble angitt for ' .. direction .. ' uten at grader også ble angitt', 2)
		elseif hemisphere then
			error('En halvkule ble angitt for ' .. direction .. ' uten at grader også ble angitt', 2)
		end
		return nil
	end
	decimal = tonumber(degrees)
	if not decimal then
		error('Gradeverdien "' .. degrees .. '" angitt for ' .. direction .. ' er ugyldig', 2)
	elseif minutes and not tonumber(minutes) then
		error('Minuttverdien "' .. minutes .. '" angitt for ' .. direction .. ' er ugyldig', 2)
	elseif seconds and not tonumber(seconds) then
		error('Sekundverdien "' .. seconds .. '" angitt for ' .. direction .. ' er ugyldig', 2)
	end
	decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600
	if hemisphere then
		local multiplier = hemisphereMultipliers[direction][hemisphere]
		if not multiplier then
			error('Halvkulen "' .. hemisphere .. '" angitt for ' .. direction .. ' er ugyldig', 2)
		end
		decimal = decimal * multiplier
	end
	return decimal
end

-- 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
-- p.top, p.bottom, and their callers need to use this
function p.valueFunc(key, value)
	if value then
		value = mw.text.trim(value)
	end
	if value ~= '' or key == 'caption' or key == 'maplink' then
		return value
	elseif key == 'useWikidata' then
		return false
	end
end

local function getContainerBilde(args, map)
	if args.AlternativeMap then
		return args.AlternativeMap
	elseif args.relieff and map('relieff') ~= '' then
		return map('relieff')
	else
		return map('bilde')
	end
end

function p.top(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local width
	if not args.bredde then
		width = round((args.standardwidth or 240) * (tonumber(map('standardskala')) or 1))
	elseif mw.ustring.sub(args.bredde, -2) == 'px' then
		width = mw.ustring.sub(args.bredde, 1, -3)
	else
		width = args.bredde
	end
	local retval = args.float == 'center' and '<div class="center">' or ''
	if args.undertekst and args.undertekst ~= '' and args.ramme ~= 'infobox' then
		retval = retval .. '<div class="noviewer thumb '
		if args.float == '"left"' or args.float == 'left' or args.float == '"venstre"' or args.float == 'venstre' then
			retval = retval .. 'floatleft'
		elseif args.float == '"center"' or args.float == 'center' or args.float == '"senter"' or args.float == 'senter' or args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'tnone'
		else
			retval = retval .. 'floatright'
		end
		retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px'
		if args.ramme == 'none' then
			retval = retval .. ';border:none'
		elseif args.ramme then
			retval = retval .. ';border-color:' .. args.ramme
		end
		retval = retval .. '"><div style="position:relative;width:' .. width .. 'px' .. (args.ramme ~= 'none' and ';border:1px solid lightgray">' or '">')
	else
		retval = retval .. '<div style="width:' .. width .. 'px;'
		if args.float == '"left"' or args.float == 'left' then
			retval = retval .. 'float:left;clear:left'
		elseif args.float == '"center"' or args.float == 'center' then
			retval = retval .. 'float:none;clear:both;margin-left:auto;margin-right:auto'
		elseif args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'float:none;clear:none'
		else
			retval = retval .. 'float:right;clear:right'
		end
		retval = retval .. '"><div style="width:' .. width .. 'px;padding:0"><div style="position:relative;width:' .. width .. 'px">'
	end
	local bilde = getContainerBilde(args, map)
	retval = string.format(
		'%s[[File:%s|%spx|%s%s]]',
		retval,
		bilde,
		width,
		args.alt or args.undertekst or ((args.label or Pgname({args = {[1] = mw.title.getCurrentTitle().text, [2] = '%s%(.*%)', [3] = '', ['plain'] = 'false'}})) .. ' ligger i ' .. map('navn')),
		args.kartlenke and ('|lenke=' .. args.kartlenke) or ''
	)
	if args.overlay_bilde then
		return retval .. '<div style="position:absolute;top:0;left:0">[[Fil:' .. args.overlay_bilde .. '|' .. width .. 'px]]</div>'
	else
		return retval
	end
end

function p.bottom(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local retval = '</div>'
	
	if not args.undertekst or args.ramme == 'infoboks' or args.ramme == 'infobox' then
		if args.ramme then
			retval = retval .. '<div>'
		else
			retval = retval .. '<div style="font-size:90%;padding-top:3px">'
		end
		retval = retval
		.. (args.undertekst or (args.label or Pgname({args = {[1] = mw.title.getCurrentTitle().text, [2] = '%s%(.*%)', [3] = '', ['plain'] = 'false'}})) .. ' (' .. map('navn') .. ')')
		.. '</div>'
	elseif args.undertekst ~= ''  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
		retval = retval .. '<div class="thumbcaption"><div class="magnify">[[:File:' .. getContainerBilde(args, map) .. '| ]]</div>' .. args.undertekst .. '</div>'
	end

	if args.labelBryter then
		retval = retval .. '<span class="switcher-label" style="display:none">' .. args.labelBryter .. '</span>'
	elseif args.autoSwitcherLabel then
		retval = retval .. '<span class="switcher-label" style="display:none">Show map of ' .. map('navn') .. '</span>'
	end
	
	retval = retval .. '</div></div>'
	if args.undertekst_udefinert then
		mw.log('Removed parameter caption_undefined used.')
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		retval = retval .. '[[Kategori:Kartposisjoner med mulige feil|Side bruker fjernet parameter]]'
	end
	if map('skew') ~= '' or map('lat_skew') ~= '' or map('krysser180') ~= '' or map('type') ~= '' then
		mw.log('Removed parameter used in map definition ' .. map())
		retval = retval .. '[[Kategori:Kartposisjoner med mulige feil|Side bruker fjernet parameter]]'
	end
	if string.find(map('navn'), '|', 1, true) then
		mw.log('Pipe used in name of map definition ' .. map())
		retval = retval .. '[[Kategori:Kartposisjoner med mulige feil|Navn inneholder pipe]]'
	end
	if args.float == 'center' then
		retval = retval .. '</div>'
	end
	return retval
end

local function markOuterDiv(x, y, imageDiv, labelDiv)
	return mw.html.create('div')
		:cssText('position:absolute;top:' .. round(y, 3) .. '%;left:' .. round(x, 3) .. '%')
		:node(imageDiv)
		:node(labelDiv)
end

local function markImageDiv(mark, marksize, label, lenke, alt, tittel)
	local builder = mw.html.create('div')
		:cssText('position:absolute;left:-' .. round(marksize / 2) .. 'px;top:-' .. round(marksize / 2) .. 'px;line-height:0')
		:attr('title', tittel)
	if marksize ~= 0 then
		builder:wikitext(string.format(
			'[[Fil:%s|%dx%dpx|%s|link=%s%s]]',
			mark,
			marksize,
			marksize,
			label,
			lenke,
			alt and ('|alt=' .. alt) or ''
		))
	end
	return builder
end

local function markLabelDiv(label, label_size, label_width, position, background, x, marksize)
	if tonumber(label_size) == 0 then
		return mw.html.create('div'):cssText('font-size:0%;position:absolute'):wikitext(label)
	end
	local builder = mw.html.create('div')
		:cssText('font-size:' .. label_size .. '%;line-height:110%;position:absolute;width:' .. label_width .. 'em')
	local distance = round(marksize / 2 + 1)
	local spanCss
	if position == 'top' or position == 'topp' then -- specified top
		builder:cssText('bottom:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em;text-align:center')
	elseif position == 'bottom' or position == 'bunn' then -- specified bottom
		builder:cssText('top:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em;text-align:center')
	elseif position == 'left' or position == 'venstre' or (tonumber(x) > 70 and position ~= 'right') or (tonumber(x) > 70 and position ~= 'høyre') then -- specified left or autodetected to left
		builder:cssText('top:-0.75em;right:' .. distance .. 'px;text-align:right')
		spanCss = 'float:right'
	else -- specified right or autodetected to right
		builder:cssText('top:-0.75em;left:' .. distance .. 'px;text-align:left')
		spanCss = 'float:left'
	end
	builder = builder:tag('div')
		:css('display', 'inline')
		:cssText('padding:1px')
		:cssText(spanCss)
		:wikitext(label)
	if background then
		builder:cssText('background-color:' .. background)
	end
	return builder:done()
end

local function getX(longitude, left, right)
	local width = (right - left) % 360
	if width == 0 then
		width = 360
	end
	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
	if distanceFromLeft - width / 2 >= 180 then
		distanceFromLeft = distanceFromLeft - 360
	end
	return 100 * distanceFromLeft / width
end

local function getY(latitude, top, bottom)
	return 100 * (top - latitude) / (top - bottom)
end

function p.mark(frame, args, map)
	if not args then
		args = getArgs(frame, {wrappers = 'Mal:Kartposisjon~','Mal:Kartposisjon', 'Mal:Kartposisjon+', 'Mal:Koord+kart'})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local x, y, longitude, latitude
	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')
	if not longitude and not latitude and args.brukWikidata then
		-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't.
		local entity = mw.wikibase.getEntity()
		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
			longitude, latitude = value.longitude, value.latitude
		end
	end
	if not longitude then
		error('Ingen verdi angitt for lengdegrad')
	end
	if not latitude then
		error('Ingen verdi angitt for breddegrad')
	end
	local builder = mw.html.create()
	if (not args.lon_deg) ~= (not args.lat_deg) then
		builder:wikitext('[[Kategori:Kartposisjoner med ulike presisjoner for lengde- og breddegrad|Grader]]')
	elseif (not args.lon_min) ~= (not args.lat_min) then
		builder:wikitext('[[Kategori:Kartposisjoner med ulike presisjoner for lengde- og breddegrad|Minutter]]')
	elseif (not args.lon_sec) ~= (not args.lat_sec) then
		builder:wikitext('[[Kategori:Kartposisjoner med ulike presisjoner for lengde- og breddegrad|Sekunder]]')
	elseif (not args.lon_dir) ~= (not args.lat_dir) then
		builder:wikitext('[[Kategori:Kartposisjoner med ulike presisjoner for lengde- og breddegrad|Halvkule]]')
	elseif (not args.long) ~= (not args.lat) then
		builder:wikitext('[[Kategori:Kartposisjoner med ulike presisjoner for lengde- og breddegrad|Desimal]]')
	end
	if args.skjevhet or args.lon_shift or args.markhigh then
		mw.log('Removed parameter used in invocation.')
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		builder:wikitext('[[Kategori:Kartposisjoner med mulige feil|Side bruker fjernet parameter]]')
	end
	if map('x') ~= '' then
		x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude })))
	else
		x = tonumber(getX(longitude, map('venstre'), map('høyre')))
	end
	if map('y') ~= '' then
		y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude })))
	else
		y = tonumber(getY(latitude, map('topp'), map('bunn')))
	end
	if (x < 0 or x > 100 or y < 0 or y > 100) and not args.utenfor then
		mw.log('Mark placed outside map boundaries without outside flag set. x = ' .. x .. ', y = ' .. y)
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		builder:wikitext('[[Kategori:Kartposisjoner med mulige feil|Utsideflagg ikke satt med merke utenfor kart]]')
	end
	local mark = args.merke or map('merke')
	if mark == '' then
		mark = 'Red pog.svg'
	end
	local marksize = tonumber(args.merke_str) or tonumber(map('merke_str')) or 8
	local imageDiv = markImageDiv(mark, marksize, args.label or mw.title.getCurrentTitle().text, args.lenke or '', args.alt, args[2])
	local labelDiv
	if args.stedsnavn and args.posisjon ~= 'none' then
		labelDiv = markLabelDiv(args.stedsnavn, args.stedsnavn_str or args['tekststørrelse'] or 90, args.label_width or args.tekstbredde or 6, args.posisjon, args.farge, x, marksize)
	end
	return builder:node(markOuterDiv(x, y, imageDiv, labelDiv))
end

function p.main(frame, args, map)
	if not args then
		args = getArgs(frame, {wrappers = 'Mal:Kartposisjon ', valueFunc = p.valueFunc})
	end
	if args.brukWikidata == nil then
		args.brukWikidata = true
	end
	if not map then
		if args[1] then
			map = {}
			for mapname in string.gmatch(args[1], '[^#]+') do
				map[#map + 1] = p.getMapParams(mapname, frame)
			end
			if #map == 1 then map = map[1] end
		else
			map = p.getMapParams('Verden', frame)
		end
	end
	if type(map) == 'table' then
		local outputs = {}
		args.autoSwitcherLabel = true
		for k,v in ipairs(map) do
			outputs[k] = p.main(frame, args, v)
		end
		return '<div class="switcher-container">' .. table.concat(outputs) .. '</div>'
	else
		return p.top(frame, args, map) .. tostring( p.mark(frame, args, map) ) .. p.bottom(frame, args, map)
	end
end

function p.printCoordRegion(frame)
	local mappage = require("Modul:Kartposisjon/data/" .. frame.args[1])
	local coordregion = mappage.region
	
	return coordregion
end

function p.printWidth(frame)
	local mappage = require("Modul:Kartposisjon/data/" .. frame.args[1])
	local width = mappage.bredde or 240
	local scale = mappage.standardskala or 1
	
	local widthOut = width * scale
	return widthOut
end

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.