Modul:Coordinates: Forskjell mellom sideversjoner

Fra Wikisida.no
Hopp til navigering Hopp til søk
m (Én sideversjon ble importert)
(Retter fejl ved kategorisering i Kategori:Koordinater for hovedkvarteret er på Wikidata - der var ikke angivet hvilken Wikidata-kvalifikator der skulle bruges.)
Linje 1: Linje 1:
--[[
require('strict')
This module is intended to replace the functionality of {{Coord}} and related
local math_mod = require( "Module:Math" )
templates. It provides several methods, including


{{#invoke:Coordinates | coord }} : General function formatting and displaying
local p = {}
coordinate values.


{{#invoke:Coordinates | dec2dms }} : Simple function for converting decimal
local i18n = {
degree values to DMS format.
N = 'N',
Nlong = 'nord',
W = 'V',
Wlong = 'vest',
E = 'Ø',
Elong = 'øst',
S = 'S',
Slong = 'sør',
degrees = '° ',
minutes = '′ ',
seconds = '″ ',
geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=nb',
tooltip = 'Kart, flyfoto m.m.',
errorcat = 'Sider med feilaktige koordinattagger',
sameaswikidata = 'Sider med koordinater som samsvarer med Wikidata',
notaswikidata = 'Sider med koordinater som avviker fra Wikidata',
nowikidata = 'Sider med koordinater som mangler på Wikidata',
throughwikidata = 'Sider med koordinater fra Wikidata',
invalidFormat = 'ugyldig format ', -- 'invalid coordinate format',
invalidNSEW = 'ugyldig himmelretning, må være «N», «S», «E», «Ø», «W» eller «V»', -- 'invalid direction should be "N", "S", "E" or "W"',
invalidNS = 'ugyldig himmelretning for breddegrad, må være «N» eller «S»', -- 'could not find latitude direction (should be N or S)',
invalidEW = 'ugyldig himmelretning for lengdegrad, må være «E», «Ø», «V» eller «W»', -- 'could not find longitude direction (should be W or E) ',
noCardinalDirection = 'mangler himmelretning', -- 'no cardinal direction found in coordinates',
invalidDirection = 'ugyldig himmelretning', -- 'invalid direction',
latitude90 = 'breddegrad > 90',
longitude360 = 'lengdegrad > 360',
minSec60 = 'minutter eller sekunder > 60',
negativeCoode = 'dms-koordinater skal ikke være negative', -- 'dms coordinates should be positive',
dmIntergers = 'grader og minutter må være tall', -- 'degrees and minutes should be integers',
tooManyParam = 'for mange parametere', -- 'too many parameters for coordinates',
coordMissing = 'mangler lengdegrad eller breddegrad', -- 'latitude or longitude missing',
invalidGlobe = 'ugyldig globe : ', -- 'invalid globe:',
}
local coordParse = {
['Ø'] = 'E',
['V'] = 'W',
}


{{#invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
local globedata = {
to decimal degree format.
--[[ notes:
radius in kilometers (especially imprecise for non spheric bodies)
defaultdisplay is currently disabled, activate it ?
]]--
ariel = {radius = 580, defaultdisplay = 'dec east'},
callisto = {radius = 2410, defaultdisplay = 'dec west'},
ceres = {radius = 470, defaultdisplay = 'dec east'},
charon = {radius = 1214, defaultdisplay = 'dec east'},
deimos = {radius = 7, defaultdisplay = 'dec west'},
dione = {radius = 560, defaultdisplay = 'dec west'},
enceladus = {radius = 255, defaultdisplay = 'dec west'},
ganymede = {radius = 2634, defaultdisplay = 'dec west'},
earth = {radius = 6371, defaultdisplay = 'dms'},
europa = {radius = 1561, defaultdisplay = 'dec east'},
hyperion = {radius = 140, defaultdisplay = 'dec west'},
iapetus = {radius = 725, defaultdisplay = 'dec west'},
['io'] = {radius = 1322, defaultdisplay = 'dec west'},
jupiter = {radius = 68911, defaultdisplay = 'dec east'},
mars = {radius = 3389.5, defaultdisplay = 'dec east' },
mercury = {radius = 2439.7, defaultdisplay = 'dec west'},
mimas = {radius = 197, defaultdisplay = 'dec west'},
miranda = {radius = 335, defaultdisplay = 'dec east'},
moon = {radius = 1736, defaultdisplay = 'dec'},
neptune = {radius = 24553, defaultdisplay = 'dec east'},
oberon = {radius = 761, defaultdisplay = 'dec east'},
phoebe = {radius = 110, defaultdisplay = 'dec west'},
phobos = {radius = 11, defaultdisplay = 'dec west'},
pluto = {radius = 1185, defaultdisplay = 'dec east'},
rhea = {radius = 765, defaultdisplay = 'dec west'},
saturn = {radius = 58232, defaultdisplay = 'dec east'},
titan = {radius = 2575.5, defaultdisplay = 'dec west'},
tethys = {radius = 530, defaultdisplay = 'dec west'},
titania = {radius = 394, defaultdisplay = 'dec east'},
triton = {radius = 1353, defaultdisplay = 'dec east'},
umbriel = {radius = 584, defaultdisplay = 'dec east'},
uranus = {radius = 25266, defaultdisplay = 'dec east'},
venus = {radius = 6051.8, defaultdisplay = 'dec east'},
vesta = {radius = 260, defaultdisplay = 'dec east'}
}
globedata[''] = globedata.earth


{{#invoke:Coordinates | link }} : Export the link used to reach the tools
local wikidataglobes = { -- maps Wikidata items used in coordinate-types properties of Wikidata to globe names as used by geohack
['http://www.wikidata.org/entity/Q3343'] = 'ariel',
['http://www.wikidata.org/entity/Q3134'] = 'callisto',
['http://www.wikidata.org/entity/Q596'] = 'ceres',
['http://www.wikidata.org/entity/Q6604'] = 'charon',
['http://www.wikidata.org/entity/Q7548'] = 'deimos',
['http://www.wikidata.org/entity/Q15040'] = 'dione',
['http://www.wikidata.org/entity/Q2'] = 'earth',
['http://www.wikidata.org/entity/Q3303'] = 'enceladus',
['http://www.wikidata.org/entity/Q3143'] = 'europa',
['http://www.wikidata.org/entity/Q3169'] = 'ganymede',
['http://www.wikidata.org/entity/Q15037'] = 'hyperion',
['http://www.wikidata.org/entity/Q17958'] = 'iapetus',
['http://www.wikidata.org/entity/Q3123'] = 'io',
['http://www.wikidata.org/entity/Q319'] = 'jupiter',
['http://www.wikidata.org/entity/Q111'] = 'mars',
['http://www.wikidata.org/entity/Q308'] = 'mercury',
['http://www.wikidata.org/entity/Q15034'] = 'mimas',
['http://www.wikidata.org/entity/Q3352'] = 'miranda',
['http://www.wikidata.org/entity/Q405'] = 'moon',
['http://www.wikidata.org/entity/Q332'] = 'neptune',
['http://www.wikidata.org/entity/Q3332'] = 'oberon',
['http://www.wikidata.org/entity/Q7547'] = 'phobos',
['http://www.wikidata.org/entity/Q17975'] = 'phoebe',
['http://www.wikidata.org/entity/Q339'] = 'pluto',
['http://www.wikidata.org/entity/Q15050'] ='rhea',
['http://www.wikidata.org/entity/Q193'] = 'saturn',
['http://www.wikidata.org/entity/Q15047'] = 'tethys',
['http://www.wikidata.org/entity/Q2565'] = 'titan',
['http://www.wikidata.org/entity/Q3322'] = 'titania',
['http://www.wikidata.org/entity/Q3359'] = 'triton',
['http://www.wikidata.org/entity/Q3338'] = 'umbriel',
['http://www.wikidata.org/entity/Q324'] = 'uranus',
['http://www.wikidata.org/entity/Q313'] = 'venus',
['http://www.wikidata.org/entity/Q3030'] ='vesta',
}


]]
local wikidatathreshold = 10 -- si la distance entre coordonnées Wikipédia et Wikidata dépasse se seuil (en kilomètes), une catégorie de maintenance est ajoutée
local lang = mw.language.getContentLanguage()
local default_zoom = 13


require('strict')
local function makecat(cat, sortkey)
if type( sortkey ) == 'string' then
return '[[Category:' .. cat .. '|' .. sortkey .. ']]'
else
return '[[Category:' .. cat .. ']]'
end
end


local math_mod = require("Module:Math")
----------------------------------------
local coordinates = {};
--Error handling
local isSandbox = mw.getCurrentFrame():getTitle():find('sandkasse', 1, true);
--[[ Notes:
when errors occure a new error message is concatenated to errorstring
an error message contains an error category with a sortkey
For major errors, it can also display an error message (the error message will the usually be returned and the function terminated)
More minor errors do only add a category, so that readers are not bothered with error texts
sortkeys:
* A: invalid latitude, longitude or direction
* B: invalid globe
* C: something wrong with other parameters
* D: more than one primary coord
]]--


local current_page = mw.title.getCurrentTitle()
local errorstring = ''
local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );
local coord_link = 'https://geohack.toolforge.org/geohack.php?language=da&pagename=' .. page_name .. '&params='


--[[ Helper function, replacement for {{coord/display/title}} ]]
local function makeerror(args)
local function displaytitle(coords)
local errormessage = ''
return mw.getCurrentFrame():extensionTag{
if args.message then
name = 'indicator',
errormessage = '<strong class="error"> Koordinater : ' .. args.message .. '</strong>'
args = { name = 'coordinates' },
end
content = '<span id="coordinates">[[Wikipedia:Geografiske koordinater|Koordinater]]: ' .. coords .. '</span>'
local errorcat = ''
}
if mw.title.getCurrentTitle().namespace == 0 then
errorcat = makecat(i18n.errorcat, args.sortkey)
end
errorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messages
return nil
end
end


--[[ Helper function, used in detecting DMS formatting ]]
local function showerrors()
local function dmsTest(first, second)
return errorstring
if type(first) ~= 'string' or type(second) ~= 'string' then
return nil
end
local s = (first .. second):upper()
return s:find('^[NS][EW]$') or s:find('^[EW][NS]$')
end
end




--[[ Wrapper function to grab args, see Module:Arguments for this function's documentation. ]]
local function makeInvokeFunc(funcName)
return function (frame)
local args = require('Module:Arguments').getArgs(frame, {
wrappers = 'Template:Coord'
})
return coordinates[funcName](args, frame)
end
end


--[[ Helper function, handle optional args. ]]
-- Distance computation
local function optionalArg(arg, supplement)
function p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globe
return arg and arg .. supplement or ''
end


--[[
globe = string.lower(globe or 'earth')
Formats any error messages generated for display
]]
-- check arguments and converts degreees to radians
local function errorPrinter(errors)
local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
local result = ""
if (not latA) or (not latB) or (not longA) or (not longB) then return
for i,v in ipairs(errors) do
error('coordinates missing, can\'t compute distance')
result = result .. '<strong class="error">Koordinater: ' .. v[2] .. '</strong><br />'
end
if type(latA) ~= 'number' or type(latB) ~= 'number' or type(longA) ~= 'number' or type(longB) ~= 'number' then
error('coordinates are not numeric, can\'t compute distance')
end
if not globe or not globedata[globe] then
return error('globe: ' .. globe .. 'is not supported')
end
-- calcul de la distance angulaire en radians
local convratio = math.pi / 180 -- convertit en radians
latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
local cosangle = math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA)
if cosangle >= 1 then -- may be above one because of rounding errors
return 0
end
end
return result
local angle = math.acos(cosangle)
-- calcul de la distance en km
local radius = globedata[globe].radius
return radius * angle
end
end


--[[
function p.distance(frame)
Determine the required CSS class to display coordinates
local args = frame.args
return p._distance(
{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},
{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
args.globe)
end


Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
default is the mode as specificied by the user when calling the {{coord}} template
extraparams = extraparams or ''
mode is the display mode (dec or dms) that we will need to determine the css class for
local geohacklatitude, geohacklongitude
]]
-- format latitude and longitude for the URL
local function displayDefault(default, mode)
if tonumber(decLat) < 0 then
if default == "" then
geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
default = "dec"
else
geohacklatitude = decLat .. '_N'
end
end

if tonumber(decLong) < 0 then
if default == mode then
geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
return "geo-default"
elseif globedata[globe].defaultdisplay == 'dec west' then
geohacklongitude = decLong .. '_W'
else
else
return "geo-nondefault"
geohacklongitude = decLong .. '_E'
end
end
-- prepares the 'paramss=' parameter
local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
-- concatenate parameteres for geohack
return i18n.geohackurl ..
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
"&params=" .. geohackparams ..
(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
end
end


--[[
--HTML builder for a geohack link
specPrinter
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)

-- geohack url
Output formatter. Takes the structure generated by either parseDec
local url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
or parseDMS and formats it for inclusion on Wikipedia.
]]
-- displayed coordinates
local function specPrinter(args, coordinateSpec)
local displaycoords
local uriComponents = coordinateSpec["param"]
if string.sub(displayformat,1,3) == 'dec' then
if uriComponents == "" then
displaycoords = p.displaydec(decLat, decLong, displayformat)
-- RETURN error, should never be empty or nil
else
return "ERROR param var tom"
displaycoords = {
p.displaydmsdimension(dmsLat, displayformat),
p.displaydmsdimension(dmsLong, displayformat),
}
end
end
if args["name"] then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
-- build coordinate in h-geo / h-card microformat
local globeNode
if globe and globe ~= 'earth' then
globeNode = mw.html.create('data')
:addClass('p-globe')
:attr{ value = globe }
:done()
end
end

local geodmshtml = '<span class="geo-dms" title="Kort, flyfoto og andre data for dette sted">'
local coordNode = mw.html.create('')
.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
if objectname then
.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
coordNode = mw.html.create('span')
:addClass('h-card')
.. '</span>'

:tag('data')
local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
:addClass('p-name')
local geodeclat
:attr{ value = objectname }
if lat < 0 then
:done()
-- FIXME this breaks the pre-existing precision
geodeclat = tostring(coordinateSpec["dec-lat"]):sub(2) .. "°S"
else
geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°N"
end
end

coordNode
local long = tonumber( coordinateSpec["dec-long"] ) or 0
:tag('span')
local geodeclong
:addClass('h-geo')
if long < 0 then
:addClass('geo-' .. string.sub(displayformat,1,3))
-- FIXME does not handle unicode minus
:tag('data')
geodeclong = tostring(coordinateSpec["dec-long"]):sub(2) .. "°V"
:addClass('p-latitude')
else
:attr{ value = decLat }
geodeclong = (coordinateSpec["dec-long"] or 0) .. "°Ø"
:wikitext( displaycoords[1] )
:done()
:wikitext(", ")
:tag('data')
:addClass('p-longitude')
:attr{ value = decLong }
:wikitext( displaycoords[2] )
:done()
:node( globeNode )
:done()
-- buid GeoHack link
local root = mw.html.create('span')
:addClass('plainlinks nourlexpansion')
:attr('title', i18n.tooltip)
:wikitext('[' .. url )
:node(coordNode)
:wikitext("]")
:done()
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
local inlineText = displayinline and tostring(root) or ''
local titleText = ''
if displaytitle then
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:addClass( displayinline and 'noprint' or nil )
:node( root )
local frame = mw.getCurrentFrame()
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
end
end
return inlineText .. titleText
end


local geodechtml = '<span class="geo-dec" title="Kort, flyfoto og andre data for dette sted">'
local function zoom( extraparams )
.. geodeclat .. ' '
local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )
.. geodeclong
if zoomParam then
.. '</span>'
return zoomParam

end
local geonumhtml = '<span class="geo">'
.. coordinateSpec["dec-lat"] .. '; '
local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
.. coordinateSpec["dec-long"]
if scale then
.. '</span>'
return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25)

end
local inner = '<span class="' .. displayDefault(coordinateSpec["default"], "dms" ) .. '">' .. geodmshtml .. '</span>'
.. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
.. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">';
if extraType then

local zoomType = {
if not args["name"] then
country = 5,
inner = inner .. geodechtml
state = 6,
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span>'
adm1st = 7,
else
adm2nd = 8,
inner = inner .. '<span class="vcard">' .. geodechtml
city = 9,
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
isle = 10,
.. '<span style="display:none">&#xfeff; (<span class="fn org">'
mountain = 10,
.. args["name"] .. '</span>)</span></span></span>'
waterbody = 10,
airport = 12,
landmark = 13,
}
return zoomType[ extraType ]
end
end

local stylesheetLink = 'Modul:Coordinates' .. ( isSandbox and '/sandkasse' or '' ) .. '/styles.css'
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = stylesheetLink }
} .. '<span class="plainlinks nourlexpansion load-gadget" data-gadget="WikiMiniAtlas">[' .. coord_link .. uriComponents ..
' ' .. inner .. ']</span>'
end
end


--[[ Helper function, convert decimal to degrees ]]
--HTML builder for a geohack link
local function convert_dec2dms_d(coordinate)
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )
local d = math_mod._round( coordinate, 0 ) .. "°"
-- displayed coordinates
return d .. ""
local displaycoords
end
if string.sub(displayformat,1,3) == 'dec' then

displaycoords = p.displaydec(decLat, decLong, displayformat)
--[[ Helper function, convert decimal to degrees and minutes ]]
local function convert_dec2dms_dm(coordinate)
coordinate = math_mod._round( coordinate * 60, 0 );
local m = coordinate % 60;
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"

return d .. string.format( "%02d′", m )
end

--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
local function convert_dec2dms_dms(coordinate)
coordinate = math_mod._round( coordinate * 60 * 60, 0 );
local s = coordinate % 60
coordinate = math.floor( (coordinate - s) / 60 );
local m = coordinate % 60
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"

return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end

--[[
Helper function, convert decimal latitude or longitude to
degrees, minutes, and seconds format based on the specified precision.
]]
local function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
local coord = tonumber(coordinate)
local postfix
if coord >= 0 then
postfix = firstPostfix
else
else
postfix = secondPostfix
displaycoords = {
p.displaydmsdimension(dmsLat, displayformat),
p.displaydmsdimension(dmsLong, displayformat),
}
end
-- JSON for maplink
local jsonParams = {
type = 'Feature',
geometry = {
type ='Point',
coordinates = {
math_mod._round( decLong, 6 ), -- max precision in GeoJSON format
math_mod._round( decLat, 6 )
}
},
properties = {
['marker-color'] = "228b22",
}
}
if objectname then
jsonParams.properties.title = objectname
end
-- ajout de geoshape via externaldata
local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )
if not geoshape and displaytitle and mw.wikibase.getEntity() then
geoshape = mw.wikibase.getEntity().id
end
if geoshape then
jsonParams = {
jsonParams,
{
type = 'ExternalData',
service = 'geoshape',
ids = geoshape,
properties = {
['fill-opacity'] = 0.2
}
}
}
end
end


precision = precision:lower();
local maplink = mw.getCurrentFrame():extensionTag{
if precision == "dms" then
name = 'maplink',
return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
content = mw.text.jsonEncode( jsonParams ),
elseif precision == "dm" then
args = {
return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
text = displaycoords[1] .. ", " .. displaycoords[2],
elseif precision == "d" then
zoom = zoom( extraparams ) or default_zoom,
return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
latitude = decLat,
longitude = decLong,
}
}
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
local inlineText = displayinline and maplink or ''
local titleText = ''
if displaytitle then
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:addClass( displayinline and 'noprint' or nil )
:wikitext( maplink )
local frame = mw.getCurrentFrame()
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
end
end
return inlineText .. titleText
end
end


--[[
-- dms specific funcions
Convert DMS format into a N or E decimal coordinate
]]
local function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
local degrees = tonumber(degrees_str)
local minutes = tonumber(minutes_str) or 0
local seconds = tonumber(seconds_str) or 0


local function twoDigit( value )
local factor = 1
if ( value < 10 ) then
if direction == "S" or direction == "W" then
factor = -1
value = '0' .. lang:formatNum( value )
else
value = lang:formatNum( value )
end
end
return value
end


local precision = 0
function p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dms
if seconds_str then
local str = ''
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
local direction = valuetable.direction
elseif minutes_str and minutes_str ~= '' then
local degrees, minutes, seconds = '', '', ''
precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
local dimension

if format == 'dms long' then
direction = i18n[direction .. 'long']
else
else
precision = math.max( math_mod._precision(degrees_str), 0 );
direction = i18n[direction]
end
end

degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
if valuetable.minutes then
minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
end
if valuetable.seconds then
seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
end
return degrees .. minutes .. seconds .. direction
end
end


--[[
local function validdms(coordtable)
Checks input values to for out of range errors.
local direction = coordtable.direction
]]
local degrees = coordtable.degrees or 0
local function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
local minutes = coordtable.minutes or 0
local seconds = coordtable.seconds or 0
local errors = {};
lat_d = tonumber( lat_d ) or 0;
local dimension = coordtable.dimension
lat_m = tonumber( lat_m ) or 0;
if not dimension then
lat_s = tonumber( lat_s ) or 0;
if direction == 'N' or direction == 'S' then
long_d = tonumber( long_d ) or 0;
dimension = 'latitude'
long_m = tonumber( long_m ) or 0;
elseif direction == 'E' or direction == 'W' then
long_s = tonumber( long_s ) or 0;
dimension = 'longitude'

else
if strong then
makeerror({message = i18n.invalidNSEW, sortkey = 'A'})
if lat_d < 0 then
return false
table.insert(errors, {source, "breddegrad < 0 med halvkugleflag"})
end
end
if long_d < 0 then
table.insert(errors, {source, "længdegrad < 0 med halvkugleflag"})
end
--[[
#coordinates is inconsistent about whether this is an error. If globe: is
specified, it won't error on this condition, but otherwise it will.

For not simply disable this check.

if long_d > 180 then
table.insert(errors, {source, "breddegrad > 180 med halvkugle flag"})
end
]]
end
end


if lat_d > 90 then
if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
table.insert(errors, {source, "breddegrad > 90"})
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
return false
end
end
if lat_d < -90 then
table.insert(errors, {source, "breddegrad < -90"})
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
makeerror({message = i18n.invalidNS, sortkey = 'A'})
return false
end
end
if lat_m >= 60 then
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
table.insert(errors, {source, "breddeminutter >= 60"})
makeerror({message = i18n.invalidEW, sortkey = 'A'})
return false
end
end
if lat_m < 0 then
table.insert(errors, {source, "breddeminutter < 0"})
if dimension == 'latitude' and degrees > 90 then
makeerror({message = i18n.latitude90, sortkey = 'A'})
return false
end
end
if lat_s >= 60 then
table.insert(errors, {source, "breddesekunder >= 60"})
if dimension == 'longitude' and degrees > 360 then
makeerror({message = i18n.longitude360, sortkey = 'A'})
return false
end
end
if lat_s < 0 then
table.insert(errors, {source, "breddesekunder < 0"})
if degrees < 0 or minutes < 0 or seconds < 0 then
makeerror({message = i18n.negativeCoode, sortkey = 'A'})
return false
end
end
if long_d >= 360 then
table.insert(errors, {source, "længdegrad >= 360"})
if minutes > 60 or seconds > 60 then
makeerror({message = i18n.minSec60, sortkey = 'A'})
return false
end
if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
makeerror({message = i18n.dmIntergers, sortkey = 'A'})
return false
end
end
if long_d <= -360 then
return true
table.insert(errors, {source, "længdegrad <= -360"})
end

local function builddmsdimension(degrees, minutes, seconds, direction, dimension)
-- no error checking, done in function validdms
local dimensionobject = {}
-- direction and dimension (= latitude or longitude)
dimensionobject.direction = direction
if dimension then
dimensionobject.dimension = dimension
elseif direction == 'N' or direction == 'S' then
dimensionobject.dimension = 'latitude'
elseif direction == 'E' or direction == 'W' then
dimensionobject.dimension = 'longitude'
end
end
if long_m >= 60 then
table.insert(errors, {source, "længdeminutter >= 60"})
-- degrees, minutes, seconds
dimensionobject.degrees = tonumber(degrees)
dimensionobject.minutes = tonumber(minutes)
dimensionobject.seconds = tonumber(seconds)
if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' end
if minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' end
if seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' end
return dimensionobject
end

function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres
-- output table: { latitude=, longitude = , direction = }
if type( str ) ~= 'string' then
return nil
end
end
if long_m < 0 then
str = mw.ustring.gsub( mw.ustring.upper( str ), '%a+', coordParse )
table.insert(errors, {source, "længdeminutter < 0"})
if not tonumber( str ) and not str:find( '/' ) and str:find( '°' ) then
local str2 = mw.ustring.gsub( str, '[°″′\"\'\194\160 ]+', '/' )
-- avoid cases were there is degree ans seconds but no minutes
if not mw.ustring.find( str, '[″"]' ) or mw.ustring.find( str, '%d[′\'][ \194\160%d]' ) then
str = str2
end
end
end
if long_s >= 60 then
if not tonumber(str) and not string.find(str, '/') then
table.insert(errors, {source, "længdesekunder >= 60"})
makeerror({message = i18n.invalidFormat, sortkey= 'A'})
return nil
end
end
if long_s < 0 then
local args = mw.text.split(str, '/', true)
table.insert(errors, {source, "længdesekunder < 0"})
if #args > 4 then
makeerror({message = i18n.tooManyParam, sortkey= 'A' })
end
local direction = mw.text.trim(args[#args])
table.remove(args)
local degrees, minutes, seconds = args[1], args[2], args[3]
local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction, dimension)
if validdms(dimensionobject) then
return dimensionobject
else
return nil
end
end
end


return errors;
--- decimal specific functions
function p.displaydec(latitude, longitude, format)
local lat = lang:formatNum( latitude )
local long = lang:formatNum( longitude )
if format == 'dec west' or format == 'dec east' then
local symbolNS, symbolEW = i18n.N, i18n.E
if latitude < 0 then
symbolNS = i18n.S
lat = lat:sub( 2 )
end
if format == 'dec west' then
symbolEW = i18n.W
end
if longitude < 0 then
long = lang:formatNum( 360 + longitude )
end
return { lat .. i18n.degrees .. symbolNS, long .. i18n.degrees .. symbolEW }
else
return { lat, long }
end
end
end


--[[
parseDec


Transforms decimal format latitude and longitude into the
local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
structure to be used in displaying coordinates
dec = mw.text.trim(dec)
]]
if not dec then
local function parseDec( lat, long, format )
return nil
local coordinateSpec = {}
end
local errors = {}
if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
makeerror({'invalid coord type', sortkey = "A"})
return nil
end
local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
if not numdec then -- tries the decimal + direction format
dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
if not dec or not tonumber(dec) then
return nil
end
if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
numdec = tonumber( dec )
elseif direction == 'W' or direction == 'S' then
dec = '-' .. dec
numdec = tonumber( dec )
else
if coordtype == 'latitude' then
makeerror({message = i18n.invalidNS, sortkey = 'A'})
else
makeerror({message = i18n.invalidEW, sortkey = 'A'})
end
return nil
end
end


if not long then
if coordtype == 'latitude' and math.abs(numdec) > 90 then
return nil, {{"parseDec", "Længdegrad mangler"}}
makeerror({message = i18n.latitude90 , sortkey = 'A'})
elseif not tonumber(long) then
return nil
return nil, {{"parseDec", "Længdegrad kan ikke læses som et tal: " .. long}}
end
end
if coordtype == 'longitude' and math.abs(numdec) > 360 then
makeerror({message = i18n.longitude360 , sortkey = 'A'})
return nil
end
return dec
end


errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
-- dms/dec conversion functions
coordinateSpec["dec-lat"] = lat;
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
coordinateSpec["dec-long"] = long;
if precision >= 3 then

return 'dms'
local mode = coordinates.determineMode( lat, long );
elseif precision >=1 then
coordinateSpec["dms-lat"] = convert_dec2dms( lat, "N", "S", mode) -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
return 'dm'
coordinateSpec["dms-long"] = convert_dec2dms( long, "Ø", "V", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}

if format then
coordinateSpec.default = format
else
else
coordinateSpec.default = "dec"
return 'd'
end
end
end


return coordinateSpec, errors
local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
local precision = 0
for d, val in ipairs(decs) do
precision = math.max(precision, math_mod._precision(val))
end
return convertprecision(precision)
end
end


--[[
local function dec2dms_d(dec)
parseDMS
local degrees = math_mod._round( dec, 0 )
return degrees
end


Transforms degrees, minutes, seconds format latitude and longitude
local function dec2dms_dm(dec)
into the a structure to be used in displaying coordinates
dec = math_mod._round( dec * 60, 0 )
]]
local minutes = dec % 60
local function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
local coordinateSpec, errors, backward = {}, {}
return degrees, minutes
end


lat_f = lat_f:upper();
local function dec2dms_dms(dec)
long_f = long_f:upper();
dec = math_mod._round( dec * 60 * 60, 0 )
local seconds = dec % 60
dec = math.floor( (dec - seconds) / 60 )
local minutes = dec % 60
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
return degrees, minutes, seconds
end


-- Check if specified backward
function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
if lat_f == 'E' or lat_f == 'W' then
local degrees, minutes, seconds
lat_d, long_d, lat_m, long_m, lat_s, long_s, lat_f, long_f, backward = long_d, lat_d, long_m, lat_m, long_s, lat_s, long_f, lat_f, true;
-- vérification du globe
if not ( globe and globedata[ globe ] ) then
globe = 'earth'
end
end

errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
-- precision
if not precision or precision == '' then
if not long_d then
return nil, {{"parseDMS", "Missing longitude" }}
precision = determinedmsprec({dec})
elseif not tonumber(long_d) then
end
return nil, {{"parseDMS", "Longitude could not be parsed as a number:" .. long_d }}
if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
return makeerror({sortkey = 'C'})
end
end

local dec = tonumber(dec)
if not lat_m and not lat_s and not long_m and not long_s and #errors == 0 then
if math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
-- direction
if lat_f:upper() == 'S' then
local direction
lat_d = '-' .. lat_d;
if coordtype == 'latitude' then
end
if dec < 0 then
direction = 'S'
if long_f:upper() == 'W' then
long_d = '-' .. long_d;
else
end
direction = 'N'

end
return parseDec( lat_d, long_d, format );
elseif coordtype == 'longitude' then
if dec < 0 or globedata[globe].defaultdisplay == 'dec west' then
direction = 'W'
else
direction = 'E'
end
end
end
end

coordinateSpec["dms-lat"] = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
-- conversion
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. ((long_f == 'W') and 'V' or 'Ø')
dec = math.abs(dec) -- les coordonnées en dms sont toujours positives
coordinateSpec["dec-lat"] = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
if precision == 'dms' then
coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}
degrees, minutes, seconds = dec2dms_dms(dec)

elseif precision == 'dm' then
if format then
degrees, minutes = dec2dms_dm(dec)
coordinateSpec.default = format
else
else
coordinateSpec.default = "dms"
degrees = dec2dms_d(dec)
end
end

return builddmsdimension(degrees, minutes, seconds, direction)
return coordinateSpec, errors, backward
end
end


--[[
function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
Check the input arguments for coord to determine the kind of data being provided
local args = frame.args
and then make the necessary processing.
local dec = args[1]
]]
if not tonumber(dec) then
local function formatTest(args)
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
local result, errors
return showerrors()
local backward, primary = false, false

local function getParam(args, lim)
local ret = {}
for i = 1, lim do
ret[i] = args[i] or ''
end
return table.concat(ret, '_')
end
end

local dirpositive = string.lower(args[2] or '')
local dirnegative = string.lower(args[3] or '')
if not args[1] then
-- no lat logic
local precision = string.lower(args[4] or '')
return errorPrinter( {{"formatTest", "Breddegrad mangler"}} )
local displayformat, coordtype
elseif not tonumber(args[1]) then
-- bad lat logic
if dirpositive == 'n' or dirpositive == 'nord' then
return errorPrinter( {{"formatTest", "Umulig at læse breddegrad som et tal:" .. args[1]}} )
coordtype = 'latitude'
elseif not args[4] and not args[5] and not args[6] then
else
-- dec logic
coordtype = 'longitude'
result, errors = parseDec(args[1], args[2], args.format)
end
if not result then
if dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' then
return errorPrinter(errors);
displayformat = 'dms long'
end
end
-- formatting for geohack: geohack expects D_N_D_E notation or D;D notation
local coordobject = p._dec2dms(dec, coordtype, precision)
-- wikiminiatlas doesn't support D;D notation
if coordobject then
-- #coordinates parserfunction doesn't support negative decimals with NSWE
return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
result.param = table.concat({
math.abs(tonumber(args[1])),
((tonumber(args[1]) or 0) < 0) and 'S' or 'N',
math.abs(tonumber(args[2])),
((tonumber(args[2]) or 0) < 0) and 'W' or 'E',
args[3] or ''}, '_')
elseif dmsTest(args[4], args[8]) then
-- dms logic
result, errors, backward = parseDMS(args[1], args[2], args[3], args[4],
args[5], args[6], args[7], args[8], args.format)
if args[10] then
table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 9)
elseif dmsTest(args[3], args[6]) then
-- dm logic
result, errors, backward = parseDMS(args[1], args[2], nil, args[3],
args[4], args[5], nil, args[6], args['format'])
if args[8] then
table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 7)
elseif dmsTest(args[2], args[4]) then
-- d logic
result, errors, backward = parseDMS(args[1], nil, nil, args[2],
args[3], nil, nil, args[4], args.format)
if args[6] then
table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
end
if not result then
return errorPrinter(errors)
end
result.param = getParam(args, 5)
else
else
-- Error
return showerrors()
return errorPrinter({{"formatTest", "Ukendt argumentformat"}}) .. '[[Kategori:Sider med fejlagtige koordinatmærker]]'
end
end
result.name = args.name
end


local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
for _, v in ipairs(extra_param) do
if args[v] then
table.insert(errors, {'formatTest', 'Parameter: "' .. v .. '=" bør være "' .. v .. ':"' })
end
end


local ret = specPrinter(args, result)
function p._dms2dec(dmsobject) -- transforme une table degré minute secondes en nombre décimal
if #errors > 0 then
local direction, degrees, minutes, seconds = dmsobject.direction, dmsobject.degrees, dmsobject.minutes, dmsobject.seconds
ret = ret .. ' ' .. errorPrinter(errors) .. '[[Kategori:Sider med fejlagtige koordinatmærker]]'
local factor = 0
local precision = 0
if not minutes then minutes = 0 end
if not seconds then seconds = 0 end
if direction == "N" or direction == "E" then
factor = 1
elseif direction == "W" or direction == "S" then
factor = -1
elseif not direction then
makeerror({message = i18n.noCardinalDirection, sortkey = 'A'})
return nil
else
makeerror({message = i18n.invalidDirection, sortkey = 'A'})
return nil
end
end
return ret, backward
if dmsobject.seconds then -- vérifie la précision des données initiales
precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?
elseif dmsobject.minutes then
precision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )
else
precision = math.max( math_mod._precision(tostring(degrees), 0 ) )
end
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return math_mod._round(decimal, precision)
end
end


--[[
function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax
Generate Wikidata tracking categories.
local args = frame.args
]]
if tonumber(args[1]) then
local function makeWikidataCategories(qid)
return args[1] -- coordonnées déjà en décimal
local ret
elseif not args[2] then
local qid = qid or mw.wikibase.getEntityIdForCurrentPage()
local dmsobject = p._parsedmsstring(args[1])
if mw.wikibase and current_page.namespace == 0 then
if dmsobject then
if qid and mw.wikibase.entityExists(qid) then
return p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/N
local bestStatementsP625 = mw.wikibase.getBestStatements(qid, "P625")
else
if bestStatementsP625 and bestStatementsP625[1] then
local coordType
local snaktype = bestStatementsP625[1].mainsnak.snaktype
if args[1]:match( '[NS]' ) then
coordType = 'latitude'
if snaktype == 'value' then
-- coordinates exist both here and on Wikidata, and can be compared.
elseif args[1]:match( '[EWO]') then
coordType = 'longitude'
ret = 'Koordinater på Wikidata'
elseif snaktype == 'somevalue' then
end
ret = 'Koordinater på Wikidata er sat til ukendt værdi'
if coordType then
elseif snaktype == 'novalue' then
local result = parsedec( args[1], coordType, args.globe or 'earth' )
ret = 'Koordinater på Wikidata er sat til ingen værdi'
if result then
return result
end
end
else
local bestStatementsP159 = mw.wikibase.getBestStatements(qid, "P159")
if bestStatementsP159 and bestStatementsP159[1] then
local qualifiers = bestStatementsP159[1].qualifiers
if qualifiers and qualifiers.P625 and qualifiers.P625[1] and
qualifiers.P625[1].snaktype == 'value' then
ret = 'Koordinater for hovedkvarteret er på Wikidata'
end
end
end
if ret == nil then
-- We have to either import the coordinates to Wikidata or remove them here.
ret = 'Koordinater er ikke på Wikidata'
end
end
return showerrors()
end
end
end
else
if ret then
return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})
return string.format('[[Kategori:%s]]', ret)
else
return ''
end
end
end
end


--[[
-- Wikidata
link
local function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"

if precision < 0.016 then
Simple function to export the coordinates link for other uses.
return 'dms'

elseif precision < 1 then
Usage:
return 'dm'
{{#invoke:Coordinates | link }}

]]
function coordinates.link(frame)
return coord_link;
end

--[[
dec2dms

Wrapper to allow templates to call dec2dms directly.

Usage:
{{#invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
negative_suffix | precision }}

decimal_coordinate is converted to DMS format. If positive, the positive_suffix
is appended (typical N or E), if negative, the negative suffix is appended. The
specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail
to use.
]]
coordinates.dec2dms = makeInvokeFunc('_dec2dms')
function coordinates._dec2dms(args)
local coordinate = args[1]
local firstPostfix = args[2] or ''
local secondPostfix = args[3] or ''
local precision = args[4] or ''

return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end

--[[
Helper function to determine whether to use D, DM, or DMS
format depending on the precision of the decimal input.
]]
function coordinates.determineMode( value1, value2 )
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
if precision <= 0 then
return 'd'
elseif precision <= 2 then
return 'dm';
else
else
return 'd'
return 'dms';
end
end
end
end


--[[
local function wikidatacoords(property) -- gets coordinates from wikidata
dms2dec
property = property or 'P625'

property = mw.ustring.upper(property)
Wrapper to allow templates to call dms2dec directly.
local entity = mw.wikibase.getEntityObject()

if entity
Usage:
and entity.claims
{{#invoke:Coordinates | dms2dec | direction_flag | degrees |
and entity.claims[property]
minutes | seconds }}
and entity.claims[property][1]

and entity.claims[property][1].mainsnak
Converts DMS values specified as degrees, minutes, seconds too decimal format.
and entity.claims[property][1].mainsnak.snaktype == 'value'
direction_flag is one of N, S, E, W, and determines whether the output is
and entity.claims[property][1].mainsnak.datavalue
positive (i.e. N and E) or negative (i.e. S and W).
then
]]
local coords = entity.claims[property][1].mainsnak.datavalue.value
coordinates.dms2dec = makeInvokeFunc('_dms2dec')
return coords.latitude, coords.longitude, wikidataglobes[coords.globe], convertwikidataprecision(coords.precision or .001)
function coordinates._dms2dec(args)
end
local direction = args[1]
return nil
local degrees = args[2]
local minutes = args[3]
local seconds = args[4]

return convert_dms2dec(direction, degrees, minutes, seconds)
end
end


--[[
-- main function for displaying coordinates
coord
function p._coord(args)


Main entry point for Lua function to replace {{coord}}
-- I declare variable
local displayformat = args.format -- string: one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west'
local displayplace = string.lower(args.display or 'inline') --string: one of 'inline', 'title' or 'inline,title'
local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack
local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates
local wikidata = args.wikidata -- string: set to "true" if needed
local wikidataprop = args.wikidataprop -- Wikidata property to use, defaults to P625
local dmslatitude, dmslongitude -- table (when created)
local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams)
local trackingstring = '' -- tracking cats except error cats (already in errorstring)
local rawlat, rawlong = args.latitude, args.longitude
if rawlat == '' then rawlat = nil end
if rawlong == '' then rawlong = nil end
local globe = string.lower( args.globe or extraparams:match('globe:(%a+)') or '' ) -- string: see the globedata table for accepted values
local latitude, longitude, precision, dmslatitude, dmslongitude -- latitude and longitude in decimal / dmslatitude and dmslongitude: tables withdms coords
local maplink = true -- use maplink whenever it is possible
-- II extract coordinates from Wikitext
if (rawlat or rawlong) then
if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude
makeerror({message = i18n.coordMissing, sortkey = 'A'})
return showerrors()
end
latitude = parsedec(rawlat, 'latitude', globe)


Usage:
if latitude then -- if latitude is decimal
{{#invoke:Coordinates | coord }}
longitude = parsedec(rawlong, 'longitude', globe) -- so should be longitude
{{#invoke:Coordinates | coord | lat | long }}
precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros
{{#invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
if not latitude or not longitude then
...
if errorstring == '' then

makeerror({message = i18n.invalidFormat, sortkey = 'A'})
Refer to {{coord}} documentation page for many additional parameters and
end
configuration options.
return showerrors()
end
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
latitude, longitude = tonumber(latitude), tonumber(longitude)
else -- if latitude is not decimal try to parse it as a dms string
dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude')
if not dmslatitude or not dmslongitude then
return showerrors()
end
latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)
end
end


Note: This function provides the visual display elements of {{coord}}. In
-- III extract coordinate data from Wikidata and compare them to local data
order to load coordinates into the database, the {{#coordinates:}} parser
local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
function must also be called, this is done automatically in the Lua
if wikidata == 'true' then
version of {{coord}}.
wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataprop)
]]
coordinates.coord = makeInvokeFunc('_coord')
if wikidatalatitude and latitude and longitude then
function coordinates._coord(args)
local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
if not tonumber(args[1]) and not args[2] then
if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) < maxdistance then
args[3] = args[1]; args[1] = nil
trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
local entity = mw.wikibase.getEntityObject(args.qid)
else
if entity
trackingstring = trackingstring .. makecat(i18n.notaswikidata)
and entity.claims
end
and entity.claims.P625
end
and entity.claims.P625[1].mainsnak.snaktype == 'value'
if wikidatalatitude and not latitude then
then
latitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
local precision = entity.claims.P625[1].mainsnak.datavalue.value.precision
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
args[1] = entity.claims.P625[1].mainsnak.datavalue.value.latitude
trackingstring = trackingstring .. makecat(i18n.throughwikidata)
args[2] = entity.claims.P625[1].mainsnak.datavalue.value.longitude
end
if precision then
precision = -math_mod._round(math.log(precision)/math.log(10),0)
if latitude and not wikidatalatitude then
args[1] = math_mod._round(args[1],precision)
if mw.title.getCurrentTitle().namespace == 0 then
args[2] = math_mod._round(args[2],precision)
trackingstring = trackingstring .. makecat(i18n.nowikidata)
end
end
end
end
end
end


local contents, backward = formatTest(args)
local Notes = args.notes or ''
local Display = args.display and args.display:lower() or 'inline'


-- it and ti are short for inline,title and title,inline
-- exit if stil no latitude or no longitude
local function isInline(s)
if not latitude and not longitude then
-- Finds whether coordinates are displayed inline.
return nil -- ne rien ajouter ici pour que l'appel à cette fonction retourne bien nil en l'absence de données
return s:find('inline') ~= nil or s == 'i' or s == 'it' or s == 'ti'
end
local function isInTitle(s)
-- Finds whether coordinates are displayed in the title.
return s:find('title') ~= nil or s == 't' or s == 'it' or s == 'ti'
end
end


local function coord_wrapper(in_args)
-- IV best guesses for missing parameters
-- Calls the parser function {{#coordinates:}}.
return mw.getCurrentFrame():callParserFunction('#coordinates', in_args) or ''
--- globe
if globe == '' then
globe = 'earth'
end
end

if not globedata[globe] then
local text = ''
makeerror({message = i18n.invalidGlobe .. globe})
if isInline(Display) then
globe = 'earth'
text = text .. '<span class="geo-inline">' .. contents .. Notes .. '</span>'
end
end
if globe ~= 'earth' then
if isInTitle(Display) then
-- Add to output since indicator content is invisible to Lua later on
extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
if not isInline(Display) then
maplink = false
text = text .. '<span class="geo-inline-hidden noexcerpt">' .. contents .. Notes .. '</span>'
end
--- diplayformat
if not displayformat or displayformat == '' then
displayformat = globedata[globe].defaultdisplay
end
-- displayinline/displaytitle
local displayinline = string.find(displayplace, 'inline')
local displaytitle = string.find(displayplace, 'title')
if not displayinline and not displaytitle then
displayinline = true
if displayplace ~= '' then
makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue
end
end
text = text .. displaytitle(contents .. Notes) .. makeWikidataCategories(args.qid)
end
end
if not args.nosave then
local page_title, count = mw.title.getCurrentTitle(), 1
-- V geodata
if backward then
local geodata = ''
local tmp = {}
if latitude and longitude then
while not string.find((args[count-1] or ''), '[EW]') do tmp[count] = (args[count] or ''); count = count+1 end
local latstring, longstring = tostring(latitude), tostring(longitude)
tmp.count = count; count = 2*(count-1)
local primary = ''
while count >= tmp.count do table.insert(tmp, 1, (args[count] or '')); count = count-1 end

for i, v in ipairs(tmp) do args[i] = v end
local frame = mw.getCurrentFrame()
else
local geodataparams = {[1] = latstring, [2] = longstring, [3] = extraparams }
while count <= 9 do args[count] = (args[count] or ''); count = count+1 end
if displaytitle then
geodataparams[4] = 'primary'
end
if objectname then
geodataparams.name = objectname
end
geodata = frame:callParserFunction('#coordinates', geodataparams )
if string.find(geodata, 'error') then -- the only error that has not been caught yet is primary key
geodata = ''
makeerror({sortkey='D'})
end
end
if isInTitle(Display) and not page_title.isTalkPage and page_title.subpageText ~= 'doc' and page_title.subpageText ~= 'testcases' then args[10] = 'primary' end
args.notes, args.format, args.display = nil
text = text .. coord_wrapper(args)
end
end
return text
-- VI final output
local mainstring = ''
if maplink then
mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
else
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
end
return mainstring .. notes .. trackingstring .. geodata .. showerrors()
end
end


--[[
function p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coord
coord2text
local args = frame.args
local numericargs = {}
for i, j in ipairs(args) do
args[i] = mw.text.trim(j)
if type(i) == 'number' and args[i] ~= '' then
table.insert(numericargs, args[i])
end
end


Extracts a single value from a transclusion of {{Coord}}.
if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
args.extraparams = numericargs[#numericargs]

if #numericargs == 1 and tonumber(numericargs[1]) then
Usage:
makeerror({message = i18n.coordMissing, sortkey = 'A'})

return showerrors()
{{#invoke:Coordinates | coord2text | {{Coord}} | parameter }}
end

table.remove(numericargs)
Valid values for the second parameter are: lat (signed integer), long (signed integer), type, scale, dim, region, globe, source
end

for i, j in ipairs(numericargs) do
]]
if i <= (#numericargs / 2) then
function coordinates._coord2text(coord,type)
if not args.latitude then
if coord == '' or type == '' or not type then return nil end
args.latitude = j
type = mw.text.trim(type)
else
if type == 'lat' or type == 'long' then
args.latitude = args.latitude .. '/' .. j
local result, negative = mw.text.split((mw.ustring.match(coord,'[%.%d]+°[NS] [%.%d]+°[ØV]') or ''), ' ')
end
if type == 'lat' then
result, negative = result[1], 'S'
else
else
result, negative = result[2], 'V'
if not args.longitude then
args.longitude = j
else
args.longitude = args.longitude .. '/' .. j
end
end
end
result = mw.text.split(result, '°')
if result[2] == negative then result[1] = '-'..result[1] end
return result[1]
else
return mw.ustring.match(coord, 'params=.-_' .. type .. ':(.-)[ _]')
end
end

if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
args.latitude, args.longitude = args.longitude, args.latitude
end
return p._coord(args)
end
end


function p.Coord(frame)
function coordinates.coord2text(frame)
return p.coord(frame)
return coordinates._coord2text(frame.args[1],frame.args[2])
end
end


--[[
function p.latitude(frame) -- helper function pour infobox, à déprécier
coordinsert
local args = frame.args

local latitude = frame.args[1]
Injects some text into the Geohack link of a transclusion of {{Coord}} (if that text isn't already in the transclusion). Outputs the modified transclusion of {{Coord}}.
if latitude and mw.text.trim(latitude) ~= '' then
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.
return latitude

elseif frame.args['wikidata'] == 'true' then
Usage:
local lat, long = wikidatacoords()

return lat
{{#invoke:Coordinates | coordinsert | {{Coord}} | parameter:value | parameter:value | … }}

Do not make Geohack unhappy by inserting something which isn't mentioned in the {{Coord}} documentation.

]]
function coordinates.coordinsert(frame)
-- for the 2nd or later integer parameter (the first is the coord template, as above)
for i, v in ipairs(frame.args) do
if i ~= 1 then
-- if we cannot find in the coord_template the i_th coordinsert parameter e.g. region
if not mw.ustring.find(frame.args[1], (mw.ustring.match(frame.args[i], '^(.-:)') or '')) then
-- find from the params= up to the first possibly-present underscore
-- and append the i_th coordinsert parameter and a space
-- IDK why we're adding a space but it does seem somewhat convenient
frame.args[1] = mw.ustring.gsub(frame.args[1], '(params=.-)_? ', '%1_'..frame.args[i]..' ')
end
end
end
end
if frame.args.name then
end
-- if we can't find the vcard class
function p.longitude(frame) -- helper function pour infobox, à déprécier
if not mw.ustring.find(frame.args[1], '<span class="vcard">') then
local args = frame.args
-- take something that looks like a coord template and add the vcard span with class and fn org class
local longitude = frame.args[1]
local namestr = frame.args.name
if longitude and mw.text.trim(longitude) ~= '' then
frame.args[1] = mw.ustring.gsub(
return longitude
elseif frame.args['wikidata'] == 'true' then
frame.args[1],
'(<span class="geo%-default">)(<span[^<>]*>[^<>]*</span><span[^<>]*>[^<>]*<span[^<>]*>[^<>]*</span></span>)(</span>)',
local lat, long = wikidatacoords()
'%1<span class="vcard">%2<span style="display:none">&#xfeff; (<span class="fn org">' .. namestr .. '</span>)</span></span>%3'
return long
)
-- then find anything from coordinates parameters to the 'end' and attach the title parameter
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(&params=[^&"<>%[%] ]*) ',
'%1&title=' .. mw.uri.encode(namestr) .. ' '
)
end
end
end
end
-- replace the existing indicator with a new indicator using the modified content
frame.args[1] = mw.ustring.gsub(
frame.args[1],
'(<span class="geo%-inline[^"]*">(.+)</span>)\127[^\127]*UNIQ%-%-indicator%-%x+%-%-?QINU[^\127]*\127',
function (inline, coord) return inline .. displaytitle(coord) end
)


return frame.args[1]
end


return p
return coordinates

Sideversjonen fra 3. mai 2024 kl. 12:33

Coordinates er en Lua-modul som formaterer koordinater for visning enten i løpetekst i tittelområdet (minikartet oppe til høyre). Den lager også koordinater til #coordinates-parserfunksjonen.

Modulen henter koordinater fra Wikidata. Hvis koordinater finnes både lokalt og på Wikidata vil modulen sammenligne dem og putte siden i en av kategoriene Kategori:Sider med koordinater som samsvarer med Wikidata (0) eller Kategori:Sider med koordinater som avviker fra Wikidata (0).

Bruk

Eksporte funksjoner :

  • coordinates.coord(frame) – formaterer geografiske koordinater for visning enten i løpetekst eller i

statusindikatorområdet (oppe til høyre). Lager også koordinater til #coordinates-parserfunksjonen.

  • p.dec2dms(frame) – konverterer koordinater uttrykt i titallsystemet til seksagesimalsystemet
  • p.dms2dec(frame) – konverterer koordinater uttrykt i seksagesimalsystemet til titallsystemet
  • p.latitude(frame) – henter ut breddegrad (fra lokale malparametre eller fra Wikidata). Spesielt nyttig for infobokser
  • p.longitude(frame) – henter ut lengdegrad (fra lokale malparametre eller fra Wikidata). Spesielt nyttig for infobokser
  • p.distance(frame) – beregner avstanden mellom to punkter
  • p._coord(args) – funksjon ála p.coord til bruk i andre Lua-moduler
  • p._dms2dec(dmsobject) – funksjon ála p.dms2dec til bruk i andre Lua-moduler
  • p._dec2dms(coordtype, precision) – funksjon ála p.dec2dms til bruk i andre Lua-moduler
  • p._distance(a, b, globe) – funksjon ála p.distance til bruk i andre Lua-moduler
  • p._parsedmsstring(str, dimension) - lager en dms-tabell fra en streng av typen "48/22/16/W".

Interne funksjoner:

  • makeerror - lager feilmeldinger
  • buildHTML - formaterer resultatet for p.coord() som en GeoHack-lenke
  • buildMaplinkHTML - formaterer resultatet for p.coord() som en maplink-lenke
  • displaydmsdimension - gjør om en dms-tabell som inneholder grader, minutter, sekunder, himmelretning og koordtype (= breddegrad eller lengdegrad) til en streng av typen 48° 29'32 "N
  • validdms - sjekker at en dms-tabell er gyldig (gyldig himmelretning og gyldig koordtype, gyldige verdier for grader, minutter og sekunder)
  • builddmsdimension - lager en dms-tabell
  • displaydec - gjør om en desimalbreddegrad og en desimallengdegrad til en streng av typen "34.294, 12.321"
  • parsedec - tolker og validerer desimalkoordinater
  • convertprcision - gjør om presisjonen funnet av Module:Math.precision til "d", "dm" eller "dms"
  • convertwikidataprecision - gjør om presisjonen fra Wikidata til "d", "dm" eller "dms"
  • determinedmsprec - beregner graden av presisjon som passer best for gitt desimalkoordinater
  • dec2dms_d - konverterer et desimalt koordinat til dms med presisjon på gradnivå
  • dec2dms_dm - konverterer et desimalt koordinat til dms med presisjon på minuttnivå
  • dec2dms_dms - konverterer et desimalt koordinat til dms med presisjon på sekundnivå
  • wikidatacoords - henter koordinater fra Wikidata

Interne variabler:

  • wikidatathreshold : For sider som har koordinater både lokalt og på Wikidata: Hvis koordinatene avviker med mer enn denne avstanden (i kilometer),

havner siden i Kategori:Sider med koordinater som avviker fra Wikidata. Ellers havner den i Kategori:Sider med koordinater som samsvarer med Wikidata.

Modulavhengigheter:

  • Module:Math – For å håndtere avrunding og presisjon

Grunleggende eksempler

Grunnleggende sett støtter modulen tre måter å angi geografiske koordinater på:

  1. Med desimalgrader:
    • {{#invoke:Coordinates | coord |43.651234|-79.383333}} : 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
      ⧼validator-fatal-error⧽


      breddegrad (43.651234) og lengdegrad (-79.383333) til Toronto er angitt som desimaltall
  2. Med tradisjonell angivelse:
    • {{#invoke:Coordinates | coord |43|29|4|N|79|23|0|W}} : 43°29′4″N 79°23′0″V / 43.48444°N 79.38333°V / 43.48444; -79.38333
      ⧼validator-fatal-error⧽


      breddegrad (43/29/4/N) og lengdegrad (79/23/0/W) til Toronto er angitt som grader, (bue)minutter og (bue)sekunder
    • {{#invoke:Coordinates | coord |43/29/4/N|79/23/0/W}} : Koordinater: Umulig at læse breddegrad som et tal:43/29/4/N
      ⧼validator-fatal-error⧽


      breddegrad (43/29/4/N) og lengdegrad (79/23/0/W) til Toronto i én parameter hver
  1. Fra d:Property:P625 på Wikidata (hvis artikkelsubjektet har flere koordinater brukes det første settet)
  2. * {{#invoke:Coordinates | coord | wikidata=true}}

Som standard er utdataformatet det samme som inngangsformatet, men det kan endres ved å sende et ekstra argument |format=xxx

  1. dms for tradisjonell angivelse med grader, minutter og sekunder
  2. dms long for tradisjonell angivelse med grader, minutter og sekunder samt himmelretning fullt utskrevet
  3. dec for desimalgrader:

Valg for funksjonen p.coord

Option de la fonction p.coord (utilisable depuis Lua)

  • latitude =
  • longitude =
  • globe = (planet, hvis annen planet enn jorda)
  • format = 'dms', 'dec' eller 'dms long'
  • displaytitle = "true" for å vise koordinater i statusindikatorområdet (tittel)
  • formattitle = hvis koordinatene i statusindikatorområdet skal ha et bestemt format
  • wikidata = "true" for å hente koordinater fra Wikidata
  • wikidataprop = Wikidata-egenskap som skal brukes, standard er P625

Detaljerte eksempler

Desimalgrader

Kode Resultat Resultat
|format=dec
Resultat
|format=dms
Resultat
|format=dms long
Notes
{{#invoke:Coordinates|coord|43.651234|-79.383333}} 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


Toronto
{{#invoke:Coordinates|coord|-33.856111|151.1925}} 33°51′22″S 151°11′33″Ø / 33.856111°S 151.1925°Ø / -33.856111; 151.1925
⧼validator-fatal-error⧽


33°51′22″S 151°11′33″Ø / 33.856111°S 151.1925°Ø / -33.856111; 151.1925
⧼validator-fatal-error⧽


33°51′22″S 151°11′33″Ø / 33.856111°S 151.1925°Ø / -33.856111; 151.1925
⧼validator-fatal-error⧽


33°51′22″S 151°11′33″Ø / 33.856111°S 151.1925°Ø / -33.856111; 151.1925
⧼validator-fatal-error⧽


Sydney
{{#invoke:Coordinates|coord|43.65|-79.38}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, med lavere presisjon
{{#invoke:Coordinates|coord|43.6500|-79.3800}} 43°39′00″N 79°22′48″V / 43.6500°N 79.3800°V / 43.6500; -79.3800
⧼validator-fatal-error⧽


43°39′00″N 79°22′48″V / 43.6500°N 79.3800°V / 43.6500; -79.3800
⧼validator-fatal-error⧽


43°39′00″N 79°22′48″V / 43.6500°N 79.3800°V / 43.6500; -79.3800
⧼validator-fatal-error⧽


43°39′00″N 79°22′48″V / 43.6500°N 79.3800°V / 43.6500; -79.3800
⧼validator-fatal-error⧽


Toronto, med flere nuller for å angi høyere presisjon
{{#invoke:Coordinates|coord|43.651234|N|79.383333|W}} 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V / 43.651234; -79.383333
⧼validator-fatal-error⧽


Toronto, med N/W i stedet for +/-

Tradisjonell angivelse

Kode Resultat Resultat
|format=dec
Resultat
|format=dms
Resultat
|format=dms long
Notes
{{#invoke:Coordinates|coord|43|39|N|79|23|W}} 43°39′N 79°23′V / 43.650°N 79.383°V / 43.650; -79.383
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.650°N 79.383°V / 43.650; -79.383
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.650°N 79.383°V / 43.650; -79.383
⧼validator-fatal-error⧽


43°39′N 79°23′V / 43.650°N 79.383°V / 43.650; -79.383
⧼validator-fatal-error⧽


Toronto, med grader og minutter
{{#invoke:Coordinates|coord|43|39|4|N|79|23|0|W}} 43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V / 43.65111; -79.38333
⧼validator-fatal-error⧽


43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V / 43.65111; -79.38333
⧼validator-fatal-error⧽


43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V / 43.65111; -79.38333
⧼validator-fatal-error⧽


43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V / 43.65111; -79.38333
⧼validator-fatal-error⧽


Toronto, med grader, minutter og sekunder
{{#invoke:Coordinates|coord|43|39|4.5|N|79|23|0.5|W}} 43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V / 43.651250; -79.383472
⧼validator-fatal-error⧽


43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V / 43.651250; -79.383472
⧼validator-fatal-error⧽


43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V / 43.651250; -79.383472
⧼validator-fatal-error⧽


43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V / 43.651250; -79.383472
⧼validator-fatal-error⧽


Toronto, med grader, minutter, sekunder og sekunddeler
{{#invoke:Coordinates|coord|43/39/N|79/23/W}} Koordinater: Umulig at læse breddegrad som et tal:43/39/N
⧼validator-fatal-error⧽


Koordinater: Umulig at læse breddegrad som et tal:43/39/N
⧼validator-fatal-error⧽


Koordinater: Umulig at læse breddegrad som et tal:43/39/N
⧼validator-fatal-error⧽


Koordinater: Umulig at læse breddegrad som et tal:43/39/N
⧼validator-fatal-error⧽


Toronto, med hvert koordinat samlet i et felt hver

Ekstra parametre for maplink

Enkelte av de gamle GeoHack-parametrene støttes, men ikke alle.

  • Zoomnivå for kartet kan settes enten med zoom: (fra 0 til 19).

Alternativt kan type: brukes med et sett av forhåndsdefinerte verdier (type:city gir for eksempel zoomnivå 9). Det gamle GeoHack-argumentet scale: er også støttet, det konverteres til zoomnivå internt. Det gamle GeoHack-argumentet dim: er ikke støttet.

  • region: er ikke støttet av maplink (enda???). Det betyr at vi ikke kan bruke region:NO for å angi at Norgeskart skal dukke opp i lista over eksterne kart f.eks.
  • Flere parametre skilles med understrek.

Trykk på lenkene under for å se resultatet av de forskjellige verdiene

Parameter Eksempel Resultat Notes
{{#invoke:Coordinates|coord|43.65|-79.38}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, standardvisning
zoom: {{#invoke:Coordinates|coord|43.65|-79.38|zoom:5}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, med zoomnivå 5 for å vise hele landet i kartvisningen
scale: {{#invoke:Coordinates|coord|43.65|-79.38|scale:3000000}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, med skala 3000000 for å vise hele landet i kartvisningen
dim: {{#invoke:Coordinates|coord|40.6892|-74.0445|dim:100}} 40°41′21″N 74°02′40″V / 40.6892°N 74.0445°V / 40.6892; -74.0445
⧼validator-fatal-error⧽


Frihetsgudinnen, med dimensjon 100 for å gi en passende skala (ikke støttet)
type: {{#invoke:Coordinates|coord|43.65|-79.38|type:city}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, med en skala som typisk passer for en by (type:city tilsvarer zoomnivå 9)
region: {{#invoke:Coordinates|coord|43.65|-79.38|region:CA}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38
⧼validator-fatal-error⧽


Toronto, ved å angi region:CA kan det vises karttjenester som er spesielt tilpasset Canada (ikke støttet)
globe: {{#invoke:Coordinates|coord|9.7|-20.0|globe:moon}} 9°42′N 20°00′V / 9.7°N 20.0°V / 9.7; -20.0
⧼validator-fatal-error⧽


Copernicus (månekrater), med kartlag for månen
name= {{#invoke:Coordinates|coord|43.65|-79.38|name=Toronto}} 43°39′N 79°23′V / 43.65°N 79.38°V / 43.65; -79.38 (Toronto)
⧼validator-fatal-error⧽


Toronto, med et navn som vises når du trykker på pekeren

Visning i statusindikatorområdet (tittellinjen)

Bruk |display= for å endre hvor koordinatene vises:

  • {{#invoke:Coordinates|coord|43.65|-79.38|display=inline}} : Vis bare i løpetekst (standard)
  • {{#invoke:Coordinates|coord|43.65|-79.38|display=title}} : Vis bare i statusindikatorområdet
  • {{#invoke:Coordinates|coord|43.65|-79.38|display=inline,title}} : Vis begge steder

For å vise koordinatene i statusindikatorområdet på et annet format går det an å bruke |formatitle:

  • {{#invoke:Coordinates|coord|43.65|-79.38|display=inline,title|format=dec|formatitle=dms}} : Koordinatene vises som desimalgrader i løpeteksten, men på tradisjonelt format i statusindikatorområdet

Feilmeldinger

Modulen viser en feilmelding hvis parametrene ikke utgjør gyldige koordinater.

Eksempel på feilaktig bruk

Sider med feilaktig bruk havner i Kategori:Sider med feilaktige koordinattagger.

Bruk av andre funksjoner

Konvertering fra desimalgrader til seksagesimal

{{#invoke:Coordinates | dec2dms | verdi | positiv retning | negativ retning | presisjon}}

  • verdi : desimaltall
  • positiv retning : positiv himmelretning (N for breddegrad / E for lengdegrad)
  • negativ retning : negativ himmelretning (S for breddegrad / W for lengdegrad)
  • presisjon : D, DM eller DMS
Eksempel
  • {{#invoke:Coordinates|dec2dms|43.651234|N|S|DMS}} : 43°39′04″N
  • {{#invoke:Coordinates|dec2dms|43.651234|Ø|V|DM}} : 43°39′Ø

Konvertering fra seksagesimal til desimal

{{#invoke:Coordinates | dms2dec | retning | grader | minutter | sekunder}}

  • retning: himmelretning (N/S/V/Ø)
  • grader, minutter, sekunder
Eksempel
  • {{#invoke:Coordinates|dms2dec|N|43|39|4}} : 43.65111
  • {{#invoke:Coordinates|dms2dec|N|43|39}} : 43.650
  • {{#invoke:Coordinates|dms2dec|43/39/4/N}} : Lua-feil i Modul:Math, linje 172: bad argument #1 to 'upper' (string expected, got nil).
  • {{#invoke:Coordinates|dms2dec|43/39/N}} : Lua-feil i Modul:Math, linje 172: bad argument #1 to 'upper' (string expected, got nil).

Sporingskategorier



--[[
This module is intended to replace the functionality of {{Coord}} and related
templates.  It provides several methods, including

{{#invoke:Coordinates | coord }} : General function formatting and displaying
coordinate values.

{{#invoke:Coordinates | dec2dms }} : Simple function for converting decimal
degree values to DMS format.

{{#invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
to decimal degree format.

{{#invoke:Coordinates | link }} : Export the link used to reach the tools

]]

require('strict')

local math_mod = require("Module:Math")
local coordinates = {};
local isSandbox = mw.getCurrentFrame():getTitle():find('sandkasse', 1, true);

local current_page = mw.title.getCurrentTitle()
local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );
local coord_link = 'https://geohack.toolforge.org/geohack.php?language=da&pagename=' .. page_name .. '&params='

--[[ Helper function, replacement for {{coord/display/title}} ]]
local function displaytitle(coords)
	return mw.getCurrentFrame():extensionTag{
		name = 'indicator',
		args = { name = 'coordinates' },
		content = '<span id="coordinates">[[Wikipedia:Geografiske koordinater|Koordinater]]: ' .. coords .. '</span>'
	}
end

--[[ Helper function, used in detecting DMS formatting ]]
local function dmsTest(first, second)
	if type(first) ~= 'string' or type(second) ~= 'string' then
		return nil
	end
	local s = (first .. second):upper()
	return s:find('^[NS][EW]$') or s:find('^[EW][NS]$')
end


--[[ Wrapper function to grab args, see Module:Arguments for this function's documentation. ]]
local function makeInvokeFunc(funcName)
	return function (frame)
		local args = require('Module:Arguments').getArgs(frame, {
			wrappers = 'Template:Coord'
		})
		return coordinates[funcName](args, frame)
	end
end

--[[ Helper function, handle optional args. ]]
local function optionalArg(arg, supplement)
	return arg and arg .. supplement or ''
end

--[[
Formats any error messages generated for display
]]
local function errorPrinter(errors)
	local result = ""
	for i,v in ipairs(errors) do
		result = result .. '<strong class="error">Koordinater: ' .. v[2] .. '</strong><br />'
	end
	return result
end

--[[
Determine the required CSS class to display coordinates

Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
default is the mode as specificied by the user when calling the {{coord}} template
mode is the display mode (dec or dms) that we will need to determine the css class for
]]
local function displayDefault(default, mode)
	if default == "" then
		default = "dec"
	end

	if default == mode then
		return "geo-default"
	else
		return "geo-nondefault"
	end
end

--[[
specPrinter

Output formatter.  Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
]]
local function specPrinter(args, coordinateSpec)
	local uriComponents = coordinateSpec["param"]
	if uriComponents == "" then
		-- RETURN error, should never be empty or nil
		return "ERROR param var tom"
	end
	if args["name"] then
		uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
	end

	local geodmshtml = '<span class="geo-dms" title="Kort, flyfoto og andre data for dette sted">'
			.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
			.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
			.. '</span>'

	local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
	local geodeclat
	if lat < 0 then
		-- FIXME this breaks the pre-existing precision
		geodeclat = tostring(coordinateSpec["dec-lat"]):sub(2) .. "°S"
	else
		geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°N"
	end

	local long = tonumber( coordinateSpec["dec-long"] ) or 0
	local geodeclong
	if long < 0 then
		-- FIXME does not handle unicode minus
		geodeclong = tostring(coordinateSpec["dec-long"]):sub(2) .. "°V"
	else
		geodeclong = (coordinateSpec["dec-long"] or 0) .. "°Ø"
	end

	local geodechtml = '<span class="geo-dec" title="Kort, flyfoto og andre data for dette sted">'
			.. geodeclat .. ' '
			.. geodeclong
			.. '</span>'

	local geonumhtml = '<span class="geo">'
			.. coordinateSpec["dec-lat"] .. '; '
			.. coordinateSpec["dec-long"]
			.. '</span>'

	local inner = '<span class="' .. displayDefault(coordinateSpec["default"], "dms" ) .. '">' .. geodmshtml .. '</span>'
				.. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
				.. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">';

	if not args["name"] then
		inner = inner .. geodechtml
				.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span>'
	else
		inner = inner .. '<span class="vcard">' .. geodechtml
				.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
				.. '<span style="display:none">&#xfeff; (<span class="fn org">'
				.. args["name"] .. '</span>)</span></span></span>'
	end

    local stylesheetLink = 'Modul:Coordinates' .. ( isSandbox and '/sandkasse' or '' ) .. '/styles.css'
	return mw.getCurrentFrame():extensionTag{
		name = 'templatestyles', args = { src = stylesheetLink }
	} .. '<span class="plainlinks nourlexpansion load-gadget" data-gadget="WikiMiniAtlas">[' .. coord_link .. uriComponents ..
	' ' .. inner .. ']</span>'
end

--[[ Helper function, convert decimal to degrees ]]
local function convert_dec2dms_d(coordinate)
	local d = math_mod._round( coordinate, 0 ) .. "°"
	return d .. ""
end

--[[ Helper function, convert decimal to degrees and minutes ]]
local function convert_dec2dms_dm(coordinate)
	coordinate = math_mod._round( coordinate * 60, 0 );
	local m = coordinate % 60;
	coordinate = math.floor( (coordinate - m) / 60 );
	local d = coordinate % 360 .."°"

	return d .. string.format( "%02d′", m )
end

--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
local function convert_dec2dms_dms(coordinate)
	coordinate = math_mod._round( coordinate * 60 * 60, 0 );
	local s = coordinate % 60
	coordinate = math.floor( (coordinate - s) / 60 );
	local m = coordinate % 60
	coordinate = math.floor( (coordinate - m) / 60 );
	local d = coordinate % 360 .."°"

	return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end

--[[
Helper function, convert decimal latitude or longitude to
degrees, minutes, and seconds format based on the specified precision.
]]
local function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
	local coord = tonumber(coordinate)
	local postfix
	if coord >= 0 then
		postfix = firstPostfix
	else
		postfix = secondPostfix
	end

	precision = precision:lower();
	if precision == "dms" then
		return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
	elseif precision == "dm" then
		return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
	elseif precision == "d" then
		return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
	end
end

--[[
Convert DMS format into a N or E decimal coordinate
]]
local function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
	local degrees = tonumber(degrees_str)
	local minutes = tonumber(minutes_str) or 0
	local seconds = tonumber(seconds_str) or 0

	local factor = 1
	if direction == "S" or direction == "W" then
		factor = -1
	end

	local precision = 0
	if seconds_str then
		precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
	elseif minutes_str and minutes_str ~= '' then
		precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
	else
		precision = math.max( math_mod._precision(degrees_str), 0 );
	end

	local decimal = factor * (degrees+(minutes+seconds/60)/60)
	return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
end

--[[
Checks input values to for out of range errors.
]]
local function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
	local errors = {};
	lat_d = tonumber( lat_d ) or 0;
	lat_m = tonumber( lat_m ) or 0;
	lat_s = tonumber( lat_s ) or 0;
	long_d = tonumber( long_d ) or 0;
	long_m = tonumber( long_m ) or 0;
	long_s = tonumber( long_s ) or 0;

	if strong then
		if lat_d < 0 then
			table.insert(errors, {source, "breddegrad < 0 med halvkugleflag"})
		end
		if long_d < 0 then
			table.insert(errors, {source, "længdegrad < 0 med halvkugleflag"})
		end
		--[[
		#coordinates is inconsistent about whether this is an error.  If globe: is
		specified, it won't error on this condition, but otherwise it will.

		For not simply disable this check.

		if long_d > 180 then
			table.insert(errors, {source, "breddegrad > 180 med halvkugle flag"})
		end
		]]
	end

	if lat_d > 90 then
		table.insert(errors, {source, "breddegrad > 90"})
	end
	if lat_d < -90 then
		table.insert(errors, {source, "breddegrad < -90"})
	end
	if lat_m >= 60 then
		table.insert(errors, {source, "breddeminutter >= 60"})
	end
	if lat_m < 0 then
		table.insert(errors, {source, "breddeminutter < 0"})
	end
	if lat_s >= 60 then
		table.insert(errors, {source, "breddesekunder >= 60"})
	end
	if lat_s < 0 then
		table.insert(errors, {source, "breddesekunder < 0"})
	end
	if long_d >= 360 then
		table.insert(errors, {source, "længdegrad >= 360"})
	end
	if long_d <= -360 then
		table.insert(errors, {source, "længdegrad <= -360"})
	end
	if long_m >= 60 then
		table.insert(errors, {source, "længdeminutter >= 60"})
	end
	if long_m < 0 then
		table.insert(errors, {source, "længdeminutter < 0"})
	end
	if long_s >= 60 then
		table.insert(errors, {source, "længdesekunder >= 60"})
	end
	if long_s < 0 then
		table.insert(errors, {source, "længdesekunder < 0"})
	end

	return errors;
end

--[[
parseDec

Transforms decimal format latitude and longitude into the
structure to be used in displaying coordinates
]]
local function parseDec( lat, long, format )
	local coordinateSpec = {}
	local errors = {}

	if not long then
		return nil, {{"parseDec", "Længdegrad mangler"}}
	elseif not tonumber(long) then
		return nil, {{"parseDec", "Længdegrad kan ikke læses som et tal: " .. long}}
	end

	errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
	coordinateSpec["dec-lat"]  = lat;
	coordinateSpec["dec-long"] = long;

	local mode = coordinates.determineMode( lat, long );
	coordinateSpec["dms-lat"]  = convert_dec2dms( lat, "N", "S", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
	coordinateSpec["dms-long"] = convert_dec2dms( long, "Ø", "V", mode)  -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}

	if format then
		coordinateSpec.default = format
	else
		coordinateSpec.default = "dec"
	end

	return coordinateSpec, errors
end

--[[
parseDMS

Transforms degrees, minutes, seconds format latitude and longitude
into the a structure to be used in displaying coordinates
]]
local function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
	local coordinateSpec, errors, backward = {}, {}

	lat_f = lat_f:upper();
	long_f = long_f:upper();

	-- Check if specified backward
	if lat_f == 'E' or lat_f == 'W' then
		lat_d, long_d, lat_m, long_m, lat_s, long_s, lat_f, long_f, backward = long_d, lat_d, long_m, lat_m, long_s, lat_s, long_f, lat_f, true;
	end

	errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
	if not long_d then
		return nil, {{"parseDMS", "Missing longitude" }}
	elseif not tonumber(long_d) then
		return nil, {{"parseDMS", "Longitude could not be parsed as a number:" .. long_d }}
	end

	if not lat_m and not lat_s and not long_m and not long_s and #errors == 0 then
		if math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
			if lat_f:upper() == 'S' then
				lat_d = '-' .. lat_d;
			end
			if long_f:upper() == 'W' then
				long_d = '-' .. long_d;
			end

			return parseDec( lat_d, long_d, format );
		end
	end

	coordinateSpec["dms-lat"]  = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
	coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. ((long_f == 'W') and 'V' or 'Ø')
	coordinateSpec["dec-lat"]  = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
	coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}

	if format then
		coordinateSpec.default = format
	else
		coordinateSpec.default = "dms"
	end

	return coordinateSpec, errors, backward
end

--[[
Check the input arguments for coord to determine the kind of data being provided
and then make the necessary processing.
]]
local function formatTest(args)
	local result, errors
	local backward, primary = false, false

	local function getParam(args, lim)
		local ret = {}
		for i = 1, lim do
			ret[i] = args[i] or ''
		end
		return table.concat(ret, '_')
	end

	if not args[1] then
		-- no lat logic
		return errorPrinter( {{"formatTest", "Breddegrad mangler"}} )
	elseif not tonumber(args[1]) then
		-- bad lat logic
		return errorPrinter( {{"formatTest", "Umulig at læse breddegrad som et tal:" .. args[1]}} )
	elseif not args[4] and not args[5] and not args[6] then
		-- dec logic
		result, errors = parseDec(args[1], args[2], args.format)
		if not result then
			return errorPrinter(errors);
		end
		-- formatting for geohack: geohack expects D_N_D_E notation or D;D notation
		-- wikiminiatlas doesn't support D;D notation
		-- #coordinates parserfunction doesn't support negative decimals with NSWE
		result.param = table.concat({
			math.abs(tonumber(args[1])),
			((tonumber(args[1]) or 0) < 0) and 'S' or 'N',
			math.abs(tonumber(args[2])),
			((tonumber(args[2]) or 0) < 0) and 'W' or 'E',
			args[3] or ''}, '_')
	elseif dmsTest(args[4], args[8]) then
		-- dms logic
		result, errors, backward = parseDMS(args[1], args[2], args[3], args[4],
			args[5], args[6], args[7], args[8], args.format)
		if args[10] then
			table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
		end
		if not result then
			return errorPrinter(errors)
		end
		result.param = getParam(args, 9)
	elseif dmsTest(args[3], args[6]) then
		-- dm logic
		result, errors, backward = parseDMS(args[1], args[2], nil, args[3],
			args[4], args[5], nil, args[6], args['format'])
		if args[8] then
			table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
		end
		if not result then
			return errorPrinter(errors)
		end
		result.param = getParam(args, 7)
	elseif dmsTest(args[2], args[4]) then
		-- d logic
		result, errors, backward = parseDMS(args[1], nil, nil, args[2],
			args[3], nil, nil, args[4], args.format)
		if args[6] then
			table.insert(errors, {'formatTest', 'Ekstra ukendt parameter'})
		end
		if not result then
			return errorPrinter(errors)
		end
		result.param = getParam(args, 5)
	else
		-- Error
		return errorPrinter({{"formatTest", "Ukendt argumentformat"}}) .. '[[Kategori:Sider med fejlagtige koordinatmærker]]'
	end
	result.name = args.name

	local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
	for _, v in ipairs(extra_param) do
		if args[v] then
			table.insert(errors, {'formatTest', 'Parameter: "' .. v .. '=" bør være "' .. v .. ':"' })
		end
	end

	local ret = specPrinter(args, result)
	if #errors > 0 then
		ret = ret .. ' ' .. errorPrinter(errors) .. '[[Kategori:Sider med fejlagtige koordinatmærker]]'
	end
	return ret, backward
end

--[[
Generate Wikidata tracking categories.
]]
local function makeWikidataCategories(qid)
	local ret
	local qid = qid or mw.wikibase.getEntityIdForCurrentPage()
	if mw.wikibase and current_page.namespace == 0 then
		if qid and mw.wikibase.entityExists(qid) then
			local bestStatementsP625 = mw.wikibase.getBestStatements(qid, "P625")
			if bestStatementsP625 and bestStatementsP625[1] then
				local snaktype = bestStatementsP625[1].mainsnak.snaktype
				if snaktype == 'value' then
					-- coordinates exist both here and on Wikidata, and can be compared.
					ret = 'Koordinater på Wikidata'
				elseif snaktype == 'somevalue' then
					ret = 'Koordinater på Wikidata er sat til ukendt værdi'
				elseif snaktype == 'novalue' then
					ret = 'Koordinater på Wikidata er sat til ingen værdi'
				end
			else
				local bestStatementsP159 = mw.wikibase.getBestStatements(qid, "P159")
				if bestStatementsP159 and bestStatementsP159[1] then
					local qualifiers = bestStatementsP159[1].qualifiers
					if qualifiers and qualifiers.P625 and qualifiers.P625[1] and
						qualifiers.P625[1].snaktype == 'value' then
						ret = 'Koordinater for hovedkvarteret er på Wikidata'
					end
				end
			end
			if ret == nil then
				-- We have to either import the coordinates to Wikidata or remove them here.
				ret = 'Koordinater er ikke på Wikidata'
			end
		end
	end
	if ret then
		return string.format('[[Kategori:%s]]', ret)
	else
		return ''
	end
end

--[[
link

Simple function to export the coordinates link for other uses.

Usage:
	{{#invoke:Coordinates | link }}

]]
function coordinates.link(frame)
	return coord_link;
end

--[[
dec2dms

Wrapper to allow templates to call dec2dms directly.

Usage:
	{{#invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
		negative_suffix | precision }}

decimal_coordinate is converted to DMS format.  If positive, the positive_suffix
is appended (typical N or E), if negative, the negative suffix is appended.  The
specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail
to use.
]]
coordinates.dec2dms = makeInvokeFunc('_dec2dms')
function coordinates._dec2dms(args)
	local coordinate = args[1]
	local firstPostfix = args[2] or ''
	local secondPostfix = args[3] or ''
	local precision = args[4] or ''

	return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end

--[[
Helper function to determine whether to use D, DM, or DMS
format depending on the precision of the decimal input.
]]
function coordinates.determineMode( value1, value2 )
	local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
	if precision <= 0 then
		return 'd'
	elseif precision <= 2 then
		return 'dm';
	else
		return 'dms';
	end
end

--[[
dms2dec

Wrapper to allow templates to call dms2dec directly.

Usage:
	{{#invoke:Coordinates | dms2dec | direction_flag | degrees |
		minutes | seconds }}

Converts DMS values specified as degrees, minutes, seconds too decimal format.
direction_flag is one of N, S, E, W, and determines whether the output is
positive (i.e. N and E) or negative (i.e. S and W).
]]
coordinates.dms2dec = makeInvokeFunc('_dms2dec')
function coordinates._dms2dec(args)
	local direction = args[1]
	local degrees = args[2]
	local minutes = args[3]
	local seconds = args[4]

	return convert_dms2dec(direction, degrees, minutes, seconds)
end

--[[
coord

Main entry point for Lua function to replace {{coord}}

Usage:
	{{#invoke:Coordinates | coord }}
	{{#invoke:Coordinates | coord | lat | long }}
	{{#invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
	...

	Refer to {{coord}} documentation page for many additional parameters and
	configuration options.

Note: This function provides the visual display elements of {{coord}}.  In
order to load coordinates into the database, the {{#coordinates:}} parser
function must also be called, this is done automatically in the Lua
version of {{coord}}.
]]
coordinates.coord = makeInvokeFunc('_coord')
function coordinates._coord(args)
	if not tonumber(args[1]) and not args[2] then
		args[3] = args[1]; args[1] = nil
		local entity = mw.wikibase.getEntityObject(args.qid)
		if entity
			and entity.claims
			and entity.claims.P625
			and entity.claims.P625[1].mainsnak.snaktype == 'value'
		then
			local precision = entity.claims.P625[1].mainsnak.datavalue.value.precision
			args[1] = entity.claims.P625[1].mainsnak.datavalue.value.latitude
			args[2] = entity.claims.P625[1].mainsnak.datavalue.value.longitude
			if precision then
				precision = -math_mod._round(math.log(precision)/math.log(10),0)
				args[1] = math_mod._round(args[1],precision)
				args[2] = math_mod._round(args[2],precision)
			end
		end
	end

	local contents, backward = formatTest(args)
	local Notes = args.notes or ''
	local Display = args.display and args.display:lower() or 'inline'

	-- it and ti are short for inline,title and title,inline
	local function isInline(s)
		-- Finds whether coordinates are displayed inline.
		return s:find('inline') ~= nil or s == 'i' or s == 'it' or s == 'ti'
	end
	local function isInTitle(s)
		-- Finds whether coordinates are displayed in the title.
		return s:find('title') ~= nil or s == 't' or s == 'it' or s == 'ti'
	end

	local function coord_wrapper(in_args)
		-- Calls the parser function {{#coordinates:}}.
		return mw.getCurrentFrame():callParserFunction('#coordinates', in_args) or ''
	end

	local text = ''
	if isInline(Display) then
		text = text .. '<span class="geo-inline">' .. contents .. Notes .. '</span>'
	end
	if isInTitle(Display) then
		-- Add to output since indicator content is invisible to Lua later on
		if not isInline(Display) then
			text = text .. '<span class="geo-inline-hidden noexcerpt">' .. contents .. Notes .. '</span>'
		end
		text = text .. displaytitle(contents .. Notes) .. makeWikidataCategories(args.qid)
	end
	if not args.nosave then
		local page_title, count = mw.title.getCurrentTitle(), 1
		if backward then
			local tmp = {}
			while not string.find((args[count-1] or ''), '[EW]') do tmp[count] = (args[count] or ''); count = count+1 end
			tmp.count = count; count = 2*(count-1)
			while count >= tmp.count do table.insert(tmp, 1, (args[count] or '')); count = count-1 end
			for i, v in ipairs(tmp) do args[i] = v end
		else
			while count <= 9 do args[count] = (args[count] or ''); count = count+1 end
		end
		if isInTitle(Display) and not page_title.isTalkPage and page_title.subpageText ~= 'doc' and page_title.subpageText ~= 'testcases' then args[10] = 'primary' end
		args.notes, args.format, args.display = nil
		text = text .. coord_wrapper(args)
	end
	return text
end

--[[
coord2text

Extracts a single value from a transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.

Usage:

    {{#invoke:Coordinates | coord2text | {{Coord}} | parameter }}

Valid values for the second parameter are: lat (signed integer), long (signed integer), type, scale, dim, region, globe, source

]]
function coordinates._coord2text(coord,type)
	if coord == '' or type == '' or not type then return nil end
	type = mw.text.trim(type)
	if type == 'lat' or type == 'long' then
		local result, negative = mw.text.split((mw.ustring.match(coord,'[%.%d]+°[NS] [%.%d]+°[ØV]') or ''), ' ')
		if type == 'lat' then
			result, negative = result[1], 'S'
		else
			result, negative = result[2], 'V'
		end
		result = mw.text.split(result, '°')
		if result[2] == negative then result[1] = '-'..result[1] end
		return result[1]
	else
		return mw.ustring.match(coord, 'params=.-_' .. type .. ':(.-)[ _]')
	end
end

function coordinates.coord2text(frame)
	return coordinates._coord2text(frame.args[1],frame.args[2])
end

--[[
coordinsert

Injects some text into the Geohack link of a transclusion of {{Coord}} (if that text isn't already in the transclusion). Outputs the modified transclusion of {{Coord}}.
IF THE GEOHACK LINK SYNTAX CHANGES THIS FUNCTION MUST BE MODIFIED.

Usage:

    {{#invoke:Coordinates | coordinsert | {{Coord}} | parameter:value | parameter:value | … }}

Do not make Geohack unhappy by inserting something which isn't mentioned in the {{Coord}} documentation.

]]
function coordinates.coordinsert(frame)
	-- for the 2nd or later integer parameter (the first is the coord template, as above)
	for i, v in ipairs(frame.args) do
		if i ~= 1 then
			-- if we cannot find in the coord_template the i_th coordinsert parameter e.g. region
			if not mw.ustring.find(frame.args[1], (mw.ustring.match(frame.args[i], '^(.-:)') or '')) then
				-- find from the params= up to the first possibly-present underscore
				-- and append the i_th coordinsert parameter and a space
				-- IDK why we're adding a space but it does seem somewhat convenient
				frame.args[1] = mw.ustring.gsub(frame.args[1], '(params=.-)_? ', '%1_'..frame.args[i]..' ')
			end
		end
	end
	if frame.args.name then
		-- if we can't find the vcard class
		if not mw.ustring.find(frame.args[1], '<span class="vcard">') then
			-- take something that looks like a coord template and add the vcard span with class and fn org class
			local namestr = frame.args.name
			frame.args[1] = mw.ustring.gsub(
				frame.args[1],
				'(<span class="geo%-default">)(<span[^<>]*>[^<>]*</span><span[^<>]*>[^<>]*<span[^<>]*>[^<>]*</span></span>)(</span>)',
				'%1<span class="vcard">%2<span style="display:none">&#xfeff; (<span class="fn org">' .. namestr .. '</span>)</span></span>%3'
			)
			-- then find anything from coordinates parameters to the 'end' and attach the title parameter
			frame.args[1] = mw.ustring.gsub(
				frame.args[1],
				'(&params=[^&"<>%[%] ]*) ',
				'%1&title=' .. mw.uri.encode(namestr) .. ' '
			)
		end
	end
	
	-- replace the existing indicator with a new indicator using the modified content
	frame.args[1] = mw.ustring.gsub(
		frame.args[1],
		'(<span class="geo%-inline[^"]*">(.+)</span>)\127[^\127]*UNIQ%-%-indicator%-%x+%-%-?QINU[^\127]*\127',
		function (inline, coord) return inline .. displaytitle(coord) end
	)

	return frame.args[1]
end

return coordinates