This is an old revision of this page, as edited by Dragons flight (talk | contribs) at 05:29, 5 March 2013. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Revision as of 05:29, 5 March 2013 by Dragons flight (talk | contribs)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff) Module documentation[view] [edit] [history] [purge]This Lua module is used on approximately 1,370,000 pages, or roughly 2% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
This module depends on the following other modules: |
This module uses TemplateStyles: |
Note: The code which this module's main function (coord
) outputs is directly parsed and/or manipulated by Module:Location map and other functions of this module itself (coord2text
and coordinsert
). If the structure of the output changes (for example, to use the <mapframe>
and <maplink>
tags), please update the aforementioned scripts as well.
Using the module with coordinsert
When using the {{Coord}} template inside another template, like an infobox, there may be parameters (like type:airport
) which should be added automatically. To do so, do something like this:
{{#if:{{{coordinates|}}}|{{#invoke:Coordinates|coordinsert|{{{coordinates|}}}|parameter1:value1|parameter2:value2|parameter3:value3…}}|
Do not add more vertical bars |
than necessary.
Using the module with coord2text to extract latitude or longitude
Developers maintaining legacy code may need to extract latitude or longitude to use a parameters in other code, or a mathematical expression. The module's "coord2text" function can be used to extract data from the {{Coord}} template. To extract the latitude from a Coord template, use:
{{#invoke:coordinates|coord2text|{{Coord|57|18|22|N|4|27|32|E}}|lat}}
→ Lua error in package.lua at line 80: module 'Module:Wikitext' not found.
To extract the longitude, use:
{{#invoke:coordinates|coord2text|{{Coord|57|18|22|N|4|27|32|E}}|long}}
→ Lua error in package.lua at line 80: module 'Module:Wikitext' not found.
Modules using this module directly
Tracking categories
- Category:Pages with malformed coordinate tags (38)
- Category:Coordinates not on Wikidata (14,473)
- Category:Coordinates on Wikidata (1,205,815)
- Category:Coordinates on Wikidata set to no value (9)
- Category:Coordinates on Wikidata set to unknown value (29)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
-- A module that mimics the functionality of Template:Coord and its sub templates -- The attempt is to actually mimic a conversion of an often used en.wp template in the way -- that most templates will actually be converted by the wiki users. -- Attempt is not really to write a nice and proper module from scratch :D local math_mod = require "Module:Math"; local wikitext = require "Module:Wikitext" globalFrame = nil coordinates = {}; --- Replacement for {{coord/display/title}} function displaytitle (s) local l = "]: " .. s local co = mw.text.tag({name="span",contents=l,params={id="coordinates"}}) local p = {} p = "small" return mw.text.tag({name="span",contents=co,params=p}) end --- Replacement for {{coord/display/inline}} function displayinline (s) return s end --- Test if the arguments imply that DMS might be in use local dmsTest = function(first, second) local concatenated = first .. second; if concatenated == "NE" or concatenated == "NW" or concatenated == "SE" or concatenated == "SW" then return true; end return false; end --- Parse the frame assuming that it is in dec format. -- @frame -- @returns a table with all information needed to display coordinates function parseDec( lat, long, format ) local coordinateSpec = {} local errors = {} if long == "" or long == nil then return nil, {{"parseDec", "Missing longitude"}} end coordinateSpec = lat; coordinateSpec = long; local mode = coordinates.determineMode( lat, long ); coordinateSpec = convert_dec2dms( lat, "N", "S", mode) -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}} coordinateSpec = convert_dec2dms( long, "E", "W", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}} if format ~= "" then coordinateSpec.default = format else coordinateSpec.default = "dec" end errors = validate( lat, nil, nil, long, nil, nil, 'parseDec' ); return coordinateSpec, errors end function optionalArg(arg, suplement) if arg ~= nil and arg ~= "" then return arg .. suplement end return "" end function isEmpty(arg) if arg == nil or arg == "" then return true end return false end function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format ) local coordinateSpec = {} local errors = {} coordinateSpec = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f coordinateSpec = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f coordinateSpec = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}} coordinateSpec = 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 errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS' ); if isEmpty(long_d) then table.insert(errors, {"parseDMS", "Missing longitude" }) end return coordinateSpec, errors end --- BAD BAD URL escape -- replace this later with the actual helper template function urlEscape(arg) return arg:gsub("%s+", '%%20'):gsub("%<", "%%3C"):gsub("%>", "%%3E") end --- A function that prints a table of coordinate specifications to HTML function specPrinter(args, coordinateSpec) local uriComponents = coordinateSpec if uriComponents == "" then -- RETURN error, should never be empty or nil return "ERROR param was empty" end if args ~= "" and args ~= nil then uriComponents = uriComponents .. "&title=" .. urlEscape(coordinateSpec) end local geodmshtml = '<span class="geo-dms" title="Maps, aerial photos, and other data for this location">' -- .. coordinateSpec .. ' ' -- .. coordinateSpec .. '<span class="latitude">' .. coordinateSpec .. '</span> ' .. '<span class="longitude">' ..coordinateSpec .. '</span>' .. '</span>' local geodeclat = coordinateSpec if geodeclat == nil then local lat = tonumber( coordinateSpec ) or 0 if lat < 0 then -- FIXME this breaks the pre-existing precision geodeclat = coordinateSpec:sub(2) .. "°S" else geodeclat = (coordinateSpec or 0) .. "°N" end end -- FIXME ugly code duplication, but lazy :D local geodeclong = coordinateSpec if geodeclong == nil then local long = tonumber( coordinateSpec ) or 0 if long < 0 then -- FIXME does not handle unicode minus geodeclong = coordinateSpec:sub(2) .. "°W" else geodeclong = (coordinateSpec or 0) .. "°E" end end -- TODO requires DEC formatting -- TODO requires vcard local geodechtml = '<span class="geo-dec" title="Maps, aerial photos, and other data for this location">' .. geodeclat .. ' ' .. geodeclong -- .. '<span class="latitude">' .. geodeclat .. '</span> ' -- .. '<span class="longitude">' .. geodeclong .. '</span>' .. '</span>' local inner = '<span class="' .. displayDefault(coordinateSpec, "dms" ) .. '">' .. geodmshtml .. '</span>' .. '<span class="geo-multi-punct"> / </span>' .. '<span class="' .. displayDefault(coordinateSpec, "dec" ) .. '">' .. geodechtml .. '</span>' return '<span class="plainlinks nourlexpansion">' .. globalFrame:preprocess('') .. '</span>' end function errorPrinter(errors) local result = "" for i,v in ipairs(errors) do local errorHTML = '<strong class="error">' .. v .. ' in Module:Coordinates.' .. v .."()" .. '</strong>' result = result .. errorHTML .. "<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 function displayDefault(default, mode) if default == "" then default = "dec" end if default == mode then return "geo-default" else return "geo-nondefault" end end --- Check the arguments to determine what type of coordinates has been input function formatTest(args) local result, errors; if args == "" then -- no lat logic return errorPrinter( {{"formatTest", "Missing latitude"}} ) elseif args == "" and args == "" and args == "" then -- dec logic result, errors = parseDec( args, args, args ) result.param = table.concat( {args, "_N_", args, "_E_", args } ); elseif dmsTest(args, args) then -- dms logic result, errors = parseDMS( args, args, args, args, args, args, args, args, args ) result.param = table.concat( { args, args, args, args, args, args, args, args, args } , '_' ); elseif dmsTest(args, args) then -- dm logic result, errors = parseDMS( args, args, nil, args, args, args, nil, args, args ) result.param = table.concat( { args, args, args, args, args, args, args } , '_' ); elseif dmsTest(args, args) then -- d logic result, errors = parseDMS( args, nil, nil, args, args, nil, nil, args, args ) result.param = table.concat( { args, args, args, args, args } , '_' ); else -- Error return errorPrinter( {{"formatTest", "Unknown argument format"}} ) end result.name = args return specPrinter( args, result ) .. " " .. errorPrinter(errors) end function convert_dec2dms_d(coordinate) local d = math_mod._round( coordinate, 0 ) .. "°" return d .. "" end function convert_dec2dms_dm(coordinate) -- {{#expr:{{{1}}} mod 360}}°{{padleft:{{#expr:({{{1}}} * 600 round 0) mod 600 / 10 round 0}}|2|0}}′ local d = math.floor(coordinate % 360) .."°" local m = string.format( "%02d′", math_mod._round( math_mod._round(coordinate * 600, 0) % 600 / 10, 0 ) ) return d .. m end function convert_dec2dms_dms(coordinate) --{{#expr:(((({{{1|0}}}) * 3600) round 0) / 3600) mod 360}}°{{padleft:{{#expr:(((3600 * ({{{1|0}}})) round 0) / 60) mod 60}}|2|0}}′{{padleft:{{#expr:((360000 * ({{{1|0}}})) round -2) mod 6000 div 100}}|2|0}}″ local d = math.floor(coordinate % 360) .. "°" local m = string.format( "%02d′", math.floor(60 * coordinate) % 60 ) local s = string.format( "%02d″", (math_mod._round( 360000 * coordinate, -2 ) % 6000) / 100 ) return d .. m .. s end --- Convert a latitude or longitude to the DMS format function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision) -- {{Coord/dec2dms/{{{4}}}|{{#ifexpr:{{{1}}} >= 0||-}}{{{1}}}}}{{#ifexpr:{{{1}}} >= 0|{{{2}}}|{{{3}}}}} local coord = tonumber(coordinate) or 0 local postfix if coord >= 0 then postfix = firstPostfix else postfix = secondPostfix end 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 -- return "" .. globalFrame:expandTemplate{ title = 'coord/dec2dms', args = {coordinate, firstPostfix, secondPostfix, precision}} end --- Convert DMS into a N or E decimal coordinate -- @param coordinate direction. either "N" "S" "E" or "W" -- @param degrees: string or number -- @param minutes: string or number -- @param seconds: string or number -- @return a number with the N or E normalized decimal coordinate of the input function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str) local degrees = tonumber(degrees_str) or 0 local minutes = tonumber(minutes_str) or 0 local seconds = tonumber(seconds_str) or 0 -- {{#expr:{{#switch:{{{1}}}|N|E=1|S|W=-1}}*({{{2|0}}}+({{{3|0}}}+{{{4|0}}}/60)/60) round {{{precdec|{{#if:{{{4|}}}|5|{{#if:{{{3|}}}|3|0}}}}+{{precision1|{{{4|{{{3|{{{2}}}}}}}}}}}}}}}} local factor if direction == "N" or direction == "E" then factor = 1 else factor = -1 end local precision = 0 if not isEmpty(seconds_str) then precision = 5 + math.max( math_mod._precision(seconds_str), 0 ); elseif not isEmpty(minutes_str) then precision = 3 + math.max( math_mod._precision(minutes_str), 0 ); else precision = math.max( math_mod._precision(degrees_str), 0 ); end -- nil -> 0 local decimal = factor * (degrees+(minutes+seconds/60)/60) return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based. --return "" .. globalFrame:expandTemplate{ title = 'coord/dms2dec', args = {direction, degrees, minutes, seconds}} end function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source ) local errors = {}; if (tonumber(lat_d) or 0) > 90 then table.insert(errors, {source, "latd>90"}) end if (tonumber(lat_d) or 0) < -90 then table.insert(errors, {source, "latd<-90"}) end if (tonumber(lat_m) or 0) >= 60 then table.insert(errors, {source, "latm>=60"}) end if (tonumber(lat_m) or 0) < 0 then table.insert(errors, {source, "latm<0"}) end if (tonumber(lat_s) or 0) >= 60 then table.insert(errors, {source, "lats>=60"}) end if (tonumber(lat_s) or 0) < 0 then table.insert(errors, {source, "lats<0"}) end if (tonumber(long_d) or 0) >= 360 then table.insert(errors, {source, "longd>=360"}) end if (tonumber(long_d) or 0) <= -360 then table.insert(errors, {source, "longd<=-360"}) end if (tonumber(long_m) or 0) >= 60 then table.insert(errors, {source, "longm>=60"}) end if (tonumber(long_m) or 0) < 0 then table.insert(errors, {source, "longm<0"}) end if (tonumber(long_s) or 0) >= 60 then table.insert(errors, {source, "longs>=60"}) end if (tonumber(long_s) or 0) < 0 then table.insert(errors, {source, "longs<0"}) end return errors; end --- The display function we exposed to Module:Coordinates function coordinates.input(frame) globalFrame = frame; return formatTest(frame.args) end --- The dec2dms function exposed to Module:Coordinates function coordinates.dec2dms(frame) globalFrame = frame local coordinate = frame.args local firstPostfix = frame.args local secondPostfix = frame.args local precision = frame.args return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision) end 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 --- The dec2dms function exposed to Module:Coordinates function coordinates.dms2dec(frame) globalFrame = frame local direction = frame.args local degrees = frame.args local minutes = frame.args local seconds = frame.args return convert_dms2dec(direction, degrees, minutes, seconds) end --- This is used by {{coord}}. function coordinates.coord(frame) globalFrame = frame local args = frame.args for i=1,10 do if ( nil == args ) then args = "" end end args = args or ''; local contents = formatTest(args) local Notes = args.notes or "" local Display = args.display or "inline" local text if ( "title" ~= Display ) then text = displayinline(contents) else text = displaytitle(contents) end return text .. Notes end return coordinatesCategories: