Modul:Coordinates: Forskjell mellom sideversjoner
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 .. '¶ms=' |
|||
--[[ 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") .. |
|||
"¶ms=" .. 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') |
|||
.. '</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"> / </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"> / ' .. geonumhtml .. '</span></span>' |
|||
adm1st = 7, |
|||
else |
|||
adm2nd = 8, |
|||
inner = inner .. '<span class="vcard">' .. geodechtml |
|||
city = 9, |
|||
.. '<span style="display:none"> / ' .. geonumhtml .. '</span>' |
|||
isle = 10, |
|||
.. '<span style="display:none"> (<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 |
local factor = 1 |
||
if |
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 |
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 |
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 |
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 |
|||
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 '') |
|||
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 |
|||
-- 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 |
|||
if snaktype == 'value' then |
|||
-- coordinates exist both here and on Wikidata, and can be compared. |
|||
elseif args[1]:match( '[EWO]') then |
|||
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 ' |
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 |
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 |
function coordinates.coord2text(frame) |
||
return |
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 |
|||
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"> (<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], |
|||
'(¶ms=[^&"<>%[%] ]*) ', |
|||
'%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 |
return coordinates |
Sideversjonen fra 3. mai 2024 kl. 12:33

- NO LABEL (P625) (se bruk)
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 seksagesimalsystemetp.dms2dec(frame)
– konverterer koordinater uttrykt i seksagesimalsystemet til titallsystemetp.latitude(frame)
– henter ut breddegrad (fra lokale malparametre eller fra Wikidata). Spesielt nyttig for infobokserp.longitude(frame)
– henter ut lengdegrad (fra lokale malparametre eller fra Wikidata). Spesielt nyttig for infobokserp.distance(frame)
– beregner avstanden mellom to punkter
p._coord(args)
– funksjon ála p.coord til bruk i andre Lua-modulerp._dms2dec(dmsobject)
– funksjon ála p.dms2dec til bruk i andre Lua-modulerp._dec2dms(coordtype, precision)
– funksjon ála p.dec2dms til bruk i andre Lua-modulerp._distance(a, b, globe)
– funksjon ála p.distance til bruk i andre Lua-modulerp._parsedmsstring(str, dimension)
- lager en dms-tabell fra en streng av typen "48/22/16/W".
Interne funksjoner:
makeerror
- lager feilmeldingerbuildHTML
- formaterer resultatet for p.coord() som en GeoHack-lenkebuildMaplinkHTML
- formaterer resultatet for p.coord() som en maplink-lenkedisplaydmsdimension
- 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 "Nvaliddms
- sjekker at en dms-tabell er gyldig (gyldig himmelretning og gyldig koordtype, gyldige verdier for grader, minutter og sekunder)builddmsdimension
- lager en dms-tabelldisplaydec
- gjør om en desimalbreddegrad og en desimallengdegrad til en streng av typen "34.294, 12.321"parsedec
- tolker og validerer desimalkoordinaterconvertprcision
- 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 desimalkoordinaterdec2dms_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å:
- Med desimalgrader:
{{#invoke:Coordinates | coord |43.651234|-79.383333}}
: 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V⧼validator-fatal-error⧽
- breddegrad (43.651234) og lengdegrad (-79.383333) til Toronto er angitt som desimaltall
- 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⧼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
- Fra d:Property:P625 på Wikidata (hvis artikkelsubjektet har flere koordinater brukes det første settet)
- *
{{#invoke:Coordinates | coord | wikidata=true}}
Som standard er utdataformatet det samme som inngangsformatet, men det kan endres ved å sende et ekstra argument |format=xxx
- dms for tradisjonell angivelse med grader, minutter og sekunder
{{#invoke:Coordinates | coord |43.651234|-79.383333|format=dms}}
: 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V⧼validator-fatal-error⧽
- dms long for tradisjonell angivelse med grader, minutter og sekunder samt himmelretning fullt utskrevet
{{#invoke:Coordinates | coord |43.651234|-79.383333|format=dms long}}
: 43°39′04″N 79°23′00″V / 43.651234°N 79.383333°V⧼validator-fatal-error⧽
- dec for desimalgrader:
{{#invoke:Coordinates | coord |43|29|4|N|79|23|0|W|format=dec}}
: 43°29′4″N 79°23′0″V / 43.48444°N 79.38333°V⧼validator-fatal-error⧽
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
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 ⧼validator-fatal-error⧽ |
43°39′N 79°23′V / 43.650°N 79.383°V ⧼validator-fatal-error⧽ |
43°39′N 79°23′V / 43.650°N 79.383°V ⧼validator-fatal-error⧽ |
43°39′N 79°23′V / 43.650°N 79.383°V ⧼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 ⧼validator-fatal-error⧽ |
43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V ⧼validator-fatal-error⧽ |
43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V ⧼validator-fatal-error⧽ |
43°39′4″N 79°23′0″V / 43.65111°N 79.38333°V ⧼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 ⧼validator-fatal-error⧽ |
43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V ⧼validator-fatal-error⧽ |
43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V ⧼validator-fatal-error⧽ |
43°39′4.5″N 79°23′0.5″V / 43.651250°N 79.383472°V ⧼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 brukeregion: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 ⧼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 ⧼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 ⧼validator-fatal-error⧽ |
Toronto, med skala 3000000 for å vise hele landet i kartvisningen |
dim: | ⧼validator-fatal-error⧽ |
||
type: | {{#invoke:Coordinates|coord|43.65|-79.38|type:city}} | 43°39′N 79°23′V / 43.65°N 79.38°V ⧼validator-fatal-error⧽ |
Toronto, med en skala som typisk passer for en by (type:city tilsvarer zoomnivå 9) |
region: | ⧼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 ⧼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 ⧼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
- {{#invoke:Coordinates|coord|2843.65|-79.38}} : 323°39′N 79°23′V / 2843.65°N 79.38°V Koordinater: breddegrad > 90⧼validator-fatal-error⧽
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
- Kategori:Sider med feilaktige koordinattagger (0)
- Kategori:Sider med koordinater som samsvarer med Wikidata (0)
- Kategori:Sider med koordinater som avviker fra Wikidata (0)
- Kategori:Sider med koordinater som mangler på Wikidata (0)
- Kategori:Sider med koordinater fra Wikidata (0)
--[[ 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 .. '¶ms=' --[[ 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"> / </span>' .. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">'; if not args["name"] then inner = inner .. geodechtml .. '<span style="display:none"> / ' .. geonumhtml .. '</span></span>' else inner = inner .. '<span class="vcard">' .. geodechtml .. '<span style="display:none"> / ' .. geonumhtml .. '</span>' .. '<span style="display:none"> (<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"> (<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], '(¶ms=[^&"<>%[%] ]*) ', '%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