Changes
Jump to navigation
Jump to search
Line 1:
Line 1:
− This module is intended to replace the functionality of {{Coord}} and related+
− templates. It provides several methods, including+
+
+
+
+
− +
− 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+
+
+
+
+
+
+
+
+
+
+
+
− +
+
+
+
+
+
− local math_mod = require("Module:Math")+
− local coordinates = {};+
+
− +
− local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );+
− local coord_link = '//tools.wmflabs.org/geohack/geohack.php?pagename=' .. page_name .. '¶ms='+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− +
− local function displaytitle(s, notes)+
− local l = "[[Geographic coordinate system|Coordinates]]: " .. s +
− local co = '<span id="coordinates">' .. l .. notes .. '</span>'; +
− return '<span style="font-size: small;">' .. co .. '</span>'; +
− end+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− +
− local function displayinline(s, notes)+
− return s .. notes +
− end+
+
+
+
+
+
+
+
+
+
+
+
− +
− local function dmsTest(first, second)+
− if type(first) ~= 'string' or type(second) ~= 'string' then +
− return nil+
− end +
− +
− return s:find('^[NS][EW]$') or s:find('^[EW][NS]$') +
− end+
+
+
+
+
+
+
+
+
+
+
+
− --[[ Wrapper function to grab args, see Module:Arguments for this function's documentation. ]]+
− +
− return function (frame) +
− local args = require('Module:Arguments').getArgs(frame, {+
− wrappers = 'Template:Coord'
− })
− return coordinates[funcName](args, frame)
+
− --[[ Helper function, handle optional args. ]]+
−
− return arg and arg .. supplement or ''
− end
− --[[+
− Formats any error messages generated for display+
− ]]+
− +
− +
− +
− local errorHTML = '<strong class="error">Coordinates: ' .. v[2] .. '</strong>' +
− result = result .. errorHTML .. "<br />"
− return result
− --[[+
− 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
− ]]
−
− if default == "" then
− default = "dec"
− end
−
−
− return "geo-default"
− else
− return "geo-nondefault"
+
+
+
+
+
− +
− specPrinter+
+
+
+
+
+
+
+
+
+
− Output formatter. Takes the structure generated by either parseDec+
− or parseDMS and formats it for inclusion on Wikipedia.+
− ]]+
− +
− local uriComponents = coordinateSpec["param"] +
− if uriComponents == "" then+
− +
− return "ERROR param was empty"+
− end+
− if args["name"] then
− uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
− end
−
− local geodmshtml = '<span class="geo-dms" title="Maps, aerial photos, and other data for this location">'
− .. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
− .. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
− .. '</span>'
− local lat = tonumber( coordinateSpec["dec-lat"] ) or 0+
− +
− if lat < 0 then +
− -- FIXME this breaks the pre-existing precision+
− geodeclat = tostring(coordinateSpec["dec-lat"]):sub(2) .. "°S"+
+
+
− geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°N" +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− local long = tonumber( coordinateSpec["dec-long"] ) or 0+
− local geodeclong+
− if long < 0 then+
− +
− geodeclong = tostring(coordinateSpec["dec-long"]):sub(2) .. "°W" +
− else+
− geodeclong = (coordinateSpec["dec-long"] or 0) .. "°E"+
+
− local geodechtml = '<span class="geo-dec" title="Maps, aerial photos, and other data for this location">' +
− .. geodeclat .. ' '+
− .. geodeclong+
− .. '</span>'+
− +
− +
− .. 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" ) .. '">';+
− +
− +
− 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">'
−
−
− return '<span class="plainlinks nourlexpansion">' ..
− '[' .. 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 .."°"
− +
− +
− 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 )+
+
+
− +
− +
− degrees, minutes, and seconds format based on the specified precision. +
− ]]+
− +
− +
− local postfix+
− +
− postfix = firstPostfix +
− else+
− postfix = secondPostfix +
+
+
− +
− precision = precision:lower();+
−
− 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;
+
− +
− Convert DMS format into a N or E decimal coordinate+
− +
− local function convert_dms2dec(direction, degrees_str, minutes_str, seconds_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 +
+
+
− +
− +
− 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 );
− +
− local decimal = factor * (degrees+(minutes+seconds/60)/60)
−
− --[[
− 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, "latitude degrees < 0 with hemisphere flag"})+
− end+
− if long_d < 0 then+
− table.insert(errors, {source, "longitude degrees < 0 with hemisphere flag"})+
− 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, "longitude degrees > 180 with hemisphere flag"})+
− end +
− ]] +
− end +
− +
− +
− table.insert(errors, {source, "latitude degrees > 90"}) +
+
+
+
+
+
+
+
+
− if lat_d < -90 then +
− table.insert(errors, {source, "latitude degrees < -90"})+
− end +
− if lat_m >= 60 then +
− table.insert(errors, {source, "latitude minutes >= 60"})+
− end +
− if lat_m < 0 then+
− table.insert(errors, {source, "latitude minutes < 0"})+
− end +
− if lat_s >= 60 then+
− table.insert(errors, {source, "latitude seconds >= 60"})+
− end +
− if lat_s < 0 then+
− table.insert(errors, {source, "latitude seconds < 0"})
− end
− if long_d >= 360 then
− table.insert(errors, {source, "longitude degrees >= 360"})
− end
− if long_d <= -360 then
− table.insert(errors, {source, "longitude degrees <= -360"})
− end
−
− table.insert(errors, {source, "longitude minutes >= 60"})
− +
− table.insert(errors, {source, "longitude minutes < 0"}) +
− if long_s >= 60 then +
− table.insert(errors, {source, "longitude seconds >= 60"})
− end
− if long_s < 0 then
− table.insert(errors, {source, "longitude seconds < 0"})
− end
−
− return errors;
− +
− 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
−
− elseif not tonumber(long) then
−
− +
− 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, "E", "W", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}} +
− +
− +
− coordinateSpec.default = format +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− coordinateSpec.default = "dec" +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− +
+
+
− +
− 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 }}
− +
− 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
− 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 +
− +
− +
+
+
+
+
+
+
+
+
− +
− 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
−
−
− -- no lat logic
− return errorPrinter( {{"formatTest", "Missing latitude"}} )
− elseif not tonumber(args[1]) then
− -- bad lat logic
− return errorPrinter( {{"formatTest", "Unable to parse latitude as a number:" .. 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)
−
− 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', 'Extra unexpected parameters'})
− 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', 'Extra unexpected parameters'})
− +
− return errorPrinter(errors) +
+
− 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', 'Extra unexpected parameters'})
− end
− if not result then
− return errorPrinter(errors)
− result.param = getParam(args, 5)+
− else +
− +
− return errorPrinter({{"formatTest", "Unknown argument format"}})+
+
− 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 .. '=" should be "' .. v .. ':"' })+
− end+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
−
− local ret = specPrinter(args, result)
− if #errors > 0 then
− ret = ret .. ' ' .. errorPrinter(errors) .. '[[Category:Pages with malformed coordinate tags]]'
− end
− return ret, backward
− --[[+
− Generate Wikidata tracking categories.+
− ]]
− local function makeWikidataCategories(qid)
− local ret
− if mw.wikibase and current_page.namespace == 0 then
− local entity = qid and mw.wikibase.getEntityObject(qid) or mw.wikibase.getEntityObject()
− if entity and entity.claims and entity.claims.P625 and entity.claims.P625[1] then
− local snaktype = entity.claims.P625[1].mainsnak.snaktype
− if snaktype == 'value' then
− -- coordinates exist both here and on Wikidata, and can be compared.
− ret = 'Coordinates on Wikidata'
− elseif snaktype == 'somevalue' then
− ret = 'Coordinates on Wikidata set to unknown value'
− elseif snaktype == 'novalue' then
− ret = 'Coordinates on Wikidata set to no value'
− end
− else
− -- We have to either import the coordinates to Wikidata or remove them here.
− ret = 'Coordinates not on Wikidata'
− end
− end
− if ret then
− return string.format('[[Category:%s]]', ret)
− else
− return ''
− end
− --[[
− link
− Simple function to export the coordinates link for other uses.+
− +
− Usage:+
− {{#invoke:Coordinates | link }}+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
− ]]+
− function coordinates.link(frame)
−
− --[[+
− dec2dms+
+
− Wrapper to allow templates to call dec2dms directly.+
− +
− Usage:+
− +
− 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+
− +
− ]]+
− coordinates.dec2dms = makeInvokeFunc('_dec2dms')+
− function coordinates._dec2dms(args)+
− +
− 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 )
−
−
− return 'd'
− elseif precision <= 2 then
− return 'dm';
− else
− return 'dms';
− 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 | 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 args[1] or not tonumber(args[1])) and not args[2] and mw.wikibase.getEntityObject() then+
− args[3] = args[1]; args[1] = nil+
− local entity = args.qid and mw.wikibase.getEntityObject(args.qid) or mw.wikibase.getEntityObject()+
− if entity +
− +
− 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
−
− 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)
+
+
−
− local contents, backward = formatTest(args)
− local Notes = args.notes or ''
− local Display = args.display and args.display:lower() or 'inline'
− +
− -- Finds whether coordinates are displayed inline.+
− return s:find('inline') ~= nil or s == 'i' or s == 'it' or s == 'ti' +
+
+
+
+
+
− 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' +
+
+
+
− +
− +
− +
− return mw.getCurrentFrame():callParserFunction('#coordinates', in_args) or '' +
+
+
+
+
− +
− +
− text = text .. displayinline(contents, Notes) +
− end +
− if isInTitle(Display) then+
− text = text +
− .. displaytitle(contents, Notes)+
− +
− end+
− if not args.nosave then+
− local page_title, count = mw.title.getCurrentTitle(), 1+
− if backward then +
− +
− 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
− 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(frame)
− if frame.args[1] == '' or frame.args[2] == '' or not frame.args[2] then return nil end
− frame.args[2] = mw.text.trim(frame.args[2])
− if frame.args[2] == 'lat' or frame.args[2] == 'long' then
−
− if frame.args[2] == 'lat' then
− result, negative = result[1], 'S'
− result, negative = result[2], 'W' +
− +
− result = mw.text.split(result, '°') +
− if result[2] == negative then result[1] = '-'..result[1] end+
− return result[1] +
− else+
− return mw.ustring.match(frame.args[1], 'params=.-_'..frame.args[2]..':(.-)[ _]')
+
− --[[+
− 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.
−
− ]]
−
− for i, v in ipairs(frame.args) do
− if i ~= 1 then
− if not mw.ustring.find(frame.args[1], (mw.ustring.match(frame.args[i], '^(.-:)') or '')) then
− frame.args[1] = mw.ustring.gsub(frame.args[1], '(params=.-)_? ', '%1_'..frame.args[i]..' ')
− end
− end
− +
− if not mw.ustring.find(frame.args[1], '<span class="vcard">') then+
− 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')
− frame.args[1] = mw.ustring.gsub(frame.args[1], '(¶ms=[^&"<>%[%] ]*) ', '%1&title=' .. mw.uri.encode(namestr) .. ' ')
− end
− +
+
− +
revert to Revision as of 23:12, 27 November 2018 (later edits broke something)
--[[
--[[
__ __ _ _ ____ _ _ _
| \/ | ___ __| |_ _| | ___ _ / ___|___ ___ _ __ __| (_)_ __ __ _| |_ ___ ___
| |\/| |/ _ \ / _` | | | | |/ _ (_) | / _ \ / _ \| '__/ _` | | '_ \ / _` | __/ _ \/ __|
| | | | (_) | (_| | |_| | | __/_| |__| (_) | (_) | | | (_| | | | | | (_| | || __/\__ \
|_| |_|\___/ \__,_|\__,_|_|\___(_)\____\___/ \___/|_| \__,_|_|_| |_|\__,_|\__\___||___/
{{#invoke:Coordinates | coord }} : General function formatting and displaying
This module is intended to provide functionality of {{location}} and related
templates. It was developed on Wikimedia Commons, so if you find this code on
other sites, check there for updates and discussions.
Please do not modify this code without applying the changes first at Module:Coordinates/sandbox and testing
at Module:Coordinates/sandbox/testcases and Module talk:Coordinates/sandbox/testcases.
Authors and maintainers:
* User:Jarekt
* User:Ebraminio
Functions:
*function p.LocationTemplateCore(frame)
**function p.GeoHack_link(frame)
***function p.lat_lon(frame)
****function p._deg2dms(deg,lang)
***function p.externalLink(frame)
****function p._externalLink(site, globe, latStr, lonStr, lang, attributes)
**function p._getHeading(attributes)
**function p.externalLinksSection(frame)
***function p._externalLink(site, globe, latStr, lonStr, lang, attributes)
*function p.getHeading(frame)
*function p.deg2dms(frame)
]]
]]
require('Module:No globals')
-- =======================================
-- === Dependencies ======================
-- =======================================
require('Module:No globals') -- used for debugging purposes as it detects cases of unintended global variables
local i18n = require('Module:I18n/coordinates') -- get localized translations of site names
local yesno = require('Module:Yesno')
-- =======================================
-- === Hardwired parameters ==============
-- =======================================
local current_page = mw.title.getCurrentTitle()
-- Angles associated with each abbreviation of compass point names. See [[:en:Points of the compass]]
local compass_points = {
N = 0,
NBE = 11.25,
NNE = 22.5,
NEBN = 33.75,
NE = 45,
NEBE = 56.25,
ENE = 67.5,
EBN = 78.75,
E = 90,
EBS = 101.25,
ESE = 112.5,
SEBE = 123.75,
SE = 135,
SEBS = 146.25,
SSE = 157.5,
SBE = 168.75,
S = 180,
SBW = 191.25,
SSW = 202.5,
SWBS = 213.75,
SW = 225,
SWBW = 236.25,
WSW = 247.5,
WBS = 258.75,
W = 270,
WBN = 281.25,
WNW = 292.5,
NWBW = 303.75,
NW = 315,
NWBN = 326.25,
NNW = 337.5,
NBW = 348.75,
}
--[[ Helper function, replacement for {{coord/display/title}} ]]
-- files to use for different headings
local heading_icon = {
[ 1] = 'File:Compass-icon bb N.svg',
[ 2] = 'File:Compass-icon bb NbE.svg',
[ 3] = 'File:Compass-icon bb NNE.svg',
[ 4] = 'File:Compass-icon bb NEbN.svg',
[ 5] = 'File:Compass-icon bb NE.svg',
[ 6] = 'File:Compass-icon bb NEbE.svg',
[ 7] = 'File:Compass-icon bb ENE.svg',
[ 8] = 'File:Compass-icon bb EbN.svg',
[ 9] = 'File:Compass-icon bb E.svg',
[10] = 'File:Compass-icon bb EbS.svg',
[11] = 'File:Compass-icon bb ESE.svg',
[12] = 'File:Compass-icon bb SEbE.svg',
[13] = 'File:Compass-icon bb SE.svg',
[14] = 'File:Compass-icon bb SEbS.svg',
[15] = 'File:Compass-icon bb SSE.svg',
[16] = 'File:Compass-icon bb SbE.svg',
[17] = 'File:Compass-icon bb S.svg',
[18] = 'File:Compass-icon bb SbW.svg',
[19] = 'File:Compass-icon bb SSW.svg',
[20] = 'File:Compass-icon bb SWbS.svg',
[21] = 'File:Compass-icon bb SW.svg',
[22] = 'File:Compass-icon bb SWbW.svg',
[23] = 'File:Compass-icon bb WSW.svg',
[24] = 'File:Compass-icon bb WbS.svg',
[25] = 'File:Compass-icon bb W.svg',
[26] = 'File:Compass-icon bb WbN.svg',
[27] = 'File:Compass-icon bb WNW.svg',
[28] = 'File:Compass-icon bb NWbW.svg',
[29] = 'File:Compass-icon bb NW.svg',
[30] = 'File:Compass-icon bb NWbN.svg',
[31] = 'File:Compass-icon bb NNW.svg',
[32] = 'File:Compass-icon bb NbW.svg'
}
--[[ Helper function, Replacement for {{coord/display/inline}} ]]
-- URL definitions for different sites. Strings: $lat, $lon, $lang, $attr, $page will be
-- replaced with latitude, longitude, language code, GeoHack attribution parameters and full-page-name strings.
local SiteURL = {
GeoHack = '//tools.wmflabs.org/geohack/geohack.php?pagename=$page¶ms=$lat_N_$lon_E_$attr&language=$lang',
GoogleEarth = '//tools.wmflabs.org/geocommons/earth.kml?latdegdec=$lat&londegdec=$lon&scale=10000&commons=1',
Proximityrama = '//tools.wmflabs.org/geocommons/proximityrama?latlon=$lat,$lon',
WikimediaMap = '//maps.wikimedia.org/#16/$lat/$lon',
OpenStreetMap1 = '//tools.wmflabs.org/wiwosm/osm-on-ol/commons-on-osm.php?zoom=16&lat=$lat&lon=$lon',
OpenStreetMap2 = '//tools.wmflabs.org/osm4wiki/cgi-bin/wiki/wiki-osm.pl?project=Commons&article=$page&l=$level',
GoogleMaps = {
Mars = '//www.google.com/mars/#lat=$lat&lon=$lon&zoom=8',
Moon = '//www.google.com/moon/#lat=$lat&lon=$lon&zoom=8',
Earth = '//tools.wmflabs.org/wp-world/googlmaps-proxy.php?page=http://tools.wmflabs.org/kmlexport/%3Fproject%3DCommons%26article%3D$page&l=$level&output=classic'
}
}
--[[ Helper function, used in detecting DMS formatting ]]
-- Categories
local CoorCat = {
File = '[[Category:Media with locations]]',
Gallery = '[[Category:Galleries with coordinates]]',
Category = '[[Category:Categories with coordinates]]',
local s = (first .. second):upper()
wikidata0 = '[[Category:Pages with coordinates from Wikidata]]',
wikidata1 = '[[Category:Pages with local coordinates and matching Wikidata coordinates]]',
wikidata2 = '[[Category:Pages with local coordinates and similar Wikidata coordinates]]',
wikidata3 = '[[Category:Pages with local coordinates and mismatching Wikidata coordinates]]',
wikidata4 = '[[Category:Pages with local coordinates and missing Wikidata coordinates]]',
wikidata5 = '[[Category:Pages with locations and Wikidata ID to wrong type of entry]]',
globe = '[[Category:Media with %s locations]]',
default = '[[Category:Media with default locations]]',
attribute = '[[Category:Media with erroneous geolocation attributes]]',
erroneous = '[[Category:Media with erroneous locations]]<span style="color:red;font-weight:bold">Error: Invalid parameters!</span>\n'
}
-- =======================================
-- === Local Functions ===================
-- =======================================
local function getArgs(frame)
local function makeInvokeFunc(funcName)
local args = frame.args
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction("int","lang") -- get user's chosen language
end
end
return args
end
end
local NoLatLonString = 'latitude, longitude'
local function optionalArg(arg, supplement)
local function langSwitch(list,lang)
local langList = mw.language.getFallbacksFor(lang)
table.insert(langList,1,lang)
local function errorPrinter(errors)
for i,language in ipairs(langList) do
local result = ""
if list[language] then
for i,v in ipairs(errors) do
return list[language]
end
end
end
end
end
local function add_maplink(lat, lon, marker, text)
local tstr = ''
if text then
tstr = string.format('text="%s" ', text)
local function displayDefault(default, mode)
if default == mode then
end
end
return string.format('<maplink %szoom="13" latitude="%f" longitude="%f" class="no-icon">{'..
' "type": "Feature",'..
' "geometry": { "type":"Point", "coordinates":[%f, %f] },'..
' "properties": { "marker-symbol":"%s", "marker-size": "large", "marker-color": "0050d0" }'..
'}</maplink>', tstr, lat, lon, lon, lat, marker)
end
end
--[[
local function add_maplink2(lat1, lon1, lat2, lon2)
return string.format('<maplink zoom="13" latitude="%f" longitude="%f" class="no-icon">[{'..
' "type": "Feature",'..
' "geometry": { "type":"Point", "coordinates":[%f, %f] },'..
' "properties": { "marker-symbol":"c", "marker-size": "large", "marker-color": "0050d0", "title": "Location on Wikimedia Commons" }'..
'},{'..
' "type": "Feature",'..
' "geometry": { "type":"Point", "coordinates":[%f, %f] },'..
' "properties": { "marker-symbol":"w", "marker-size": "large", "marker-color": "228b22", "title": "Location on Wikidata" }'..
'}]</maplink>', lat2, lon2, lon1, lat1, lon2, lat2)
end
local function info_box(text)
return string.format('<table class="messagebox plainlinks layouttemplate" style="border-collapse:collapse; border-width:2px; border-style:solid; width:100%%; clear: both; '..
'border-color:#f28500; background:#ffe;direction:ltr; border-left-width: 8px; ">'..
local function specPrinter(args, coordinateSpec)
'<tr>'..
'<td class="mbox-image" style="padding-left:.9em;">'..
' [[File:Commons-emblem-issue.svg|class=noviewer|45px]]</td>'..
-- RETURN error, should never be empty or nil
'<td class="mbox-text" style="">%s</td>'..
'</tr></table>', text)
end
local function mergeWithWikidata(q, lat1, lon1)
local geodeclat
-- we are given wikidata q-code so look up the coordinates
local dist_str=''
local entity
-- Wikiata coordinates
if q:match( '^[Qq]%d+$' ) then
entity = mw.wikibase.getEntity(q)
else
else
entity = q
end
q = entity.id
local v, lat2, lon2, precision
if entity then
local P625 = entity:getBestStatements( 'P625' ) -- coordinate location
local P159 = entity:getBestStatements( 'P159' ) -- headquarters location
if P625[1] and P625[1].mainsnak.datavalue.value.latitude then
v = P625[1].mainsnak.datavalue.value
elseif P159[1] and P159[1].qualifiers and P159[1].qualifiers.P625 then
v = P159[1].qualifiers.P625[1].datavalue.value
end
if v and v.globe == 'http://www.wikidata.org/entity/Q2' then
lat2 = v.latitude
lon2 = v.longitude
precision = v.precision or 1e-4
precision = math.floor(precision*111000) -- convert precision from degrees to meters and round
precision = math.max(math.min(precision,111000),5) -- bound precision to a number between 5 meters and 1 degree
end
end
end
-- compare coordinates
local cat = ''
if not lat1 or not lon1 then -- wikidata coordinates only
lat1 = lat2
lon1 = lon2
cat = CoorCat.wikidata0
elseif lat1 and lon1 and not lat2 and not lon2 then
cat = string.format('The above coordinates are missing from linked Wikidata item [[d:%s|%s]]. Click <span class=\"plainlinks\" title=\"Click to copy to wikidata\">'..
"[https://tools.wmflabs.org/quickstatements/index_old.html#v1=%s%%09P625%%09@%09.5f/%09.5f%%09S143%%09Q565 here]</span> to copy it",
q, q, q, lat1, lon1)
cat = CoorCat.wikidata4 .. info_box(cat)
elseif lat1 and lon1 and lat2 and lon2 then
-- calculate distance
local dLat = math.rad(lat1-lat2)
local dLon = math.rad(lon1-lon2)
local d = math.pow(math.sin(dLat/2),2) + math.pow(math.sin(dLon/2),2) * math.cos(math.rad(lat1)) * math.cos(math.rad(lat2))
d = 2 * math.atan2(math.sqrt(d), math.sqrt(1-d)) -- angular distance in radians
d = 6371000 * d -- radians to meters conversion
d = math.floor(d+0.5) -- rind it to even meters
local frame = mw.getCurrentFrame()
local info = frame:preprocess(add_maplink2(lat1, lon1, lat2, lon2)) -- fancy link to OSM
info = string.format("There is a discrepancy of %i meters between the above coordinates and the ones stored at linked Wikidata item [[d:%s|%s]] (%s, precision: %i m). "..
'Please reconcile them. To copy Commons coordinates to Wikidata, click <span class=\"plainlinks\" title=\"Click to copy to wikidata\">'..
"[https://tools.wmflabs.org/quickstatements/index_old.html#v1=%s%%09P625%%09@%09.5f/%09.5f%%09S143%%09Q565 here]</span>",
d, q, q, info, precision, q, lat1, lon1)
if d<20 or d<precision then -- will consider location within 20 meters or precisi0on distance as the same
cat = CoorCat.wikidata1
dist_str = string.format(' (discrepancy of %i meters between the above coordinates and the ones stored on Wikidata)', d) -- will be displayed when hovering a mouse above wikidata icon
-- FIXME does not handle unicode minus
elseif d>1000 and d>5*precision then -- locations 1 km off and 5 precision distances away are likely wrong
cat = CoorCat.wikidata3 .. info_box(info)
else
cat = CoorCat.wikidata2 .. info_box(info)
end
end
end
-- verify proper P31 (instance of). List is based on https://www.wikidata.org/wiki/Property_talk:P625
local QCodes = {
Q5 = 1, -- human
Q11879590 = 1, -- female given name
Q202444 = 1, -- given name
local geonumhtml = '<span class="geo">'
Q12308941 = 1, -- male given name
Q4167836 = 1, -- Wikimedia category
Q4167410 = 1, -- Wikimedia disambiguation page
Q783794 = 2, -- company
Q4830453 = 2, -- business enterprise
}
local s = entity:getBestStatements( 'P31' )
if s[1] and s[1].mainsnak.datavalue.value['id'] then
local instanceOf = s[1].mainsnak.datavalue.value['id']
if not args["name"] then
if QCodes[instanceOf] then
cat = '' -- wipe out categories
if QCodes[instanceOf]==1 then -- add problem category
cat = CoorCat.wikidata5
end
end
.. args["name"] .. '</span>)</span></span></span>'
end
end
return d .. string.format( "%02d′", m )
return lat1, lon1, q, cat, dist_str
end
end
--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
-- =======================================
-- === External Functions ================
-- =======================================
local p = {}
-- parse attribute variable returning desired field (used for debugging)
function p.parseAttribute(frame)
return string.match(mw.text.decode(frame.args[1]), mw.text.decode(frame.args[2]) .. ':' .. '([^_]*)') or ''
end
end
--[[
-- Helper core function for getHeading.
Helper function, convert decimal latitude or longitude to
function p._getHeading(attributes)
if attributes == nil then
return nil
local function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end
local coord = tonumber(coordinate)
local hStr = string.match(mw.text.decode(attributes), 'heading:([^_]*)')
if hStr == nil then
if coord >= 0 then
return nil
end
local hNum = tonumber( hStr )
if hNum == nil then
hStr = string.upper (hStr)
hNum = compass_points[hStr]
end
end
if hNum ~= nil then
hNum = hNum%360
if precision == "dms" then
end
end
return hNum
end
end
--[[
--[[============================================================================
Parse attribute variable returning heading field. If heading is a string than
]]
try to convert it to an angle
==============================================================================]]
local degrees = tonumber(degrees_str)
function p.getHeading(frame)
local attributes
if frame.args[1] then
attributes = frame.args[1]
elseif frame.args.attributes then
attributes = frame.args.attributes
else
return ''
end
end
local hNum = p._getHeading(attributes)
local precision = 0
if hNum == nil then
return ''
end
end
return tostring(hNum)
return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
end
end
--[[============================================================================
Helper core function for deg2dms. deg2dms can be called by templates, while
_deg2dms should be called from Lua.
Inputs:
* degree - positive coordinate in degrees
* degPrec - coordinate precision in degrees will result in different angle format
* lang - language to used when formatting the number
==============================================================================]]
function p._deg2dms(degree, degPrec, lang)
local dNum, mNum, sNum, dStr, mStr, sStr, formatStr, secPrec, c, k, d, zero
local Lang = mw.language.new(lang)
-- adjust number display based on precision
secPrec = degPrec*3600.0 -- coordinate precision in seconds
if secPrec<0.05 then -- degPrec<1.3889e-05
formatStr = '%s° %s′ %s″' -- use DD° MM′ SS.SS″ format
c = 360000
elseif secPrec<0.5 then -- 1.3889e-05<degPrec<1.3889e-04
formatStr = '%s° %s′ %s″' -- use DD° MM′ SS.S″ format
if lat_d > 90 then
c = 36000
elseif degPrec*60.0<0.5 then -- 1.3889e-04<degPrec<0.0083
formatStr = '%s° %s′ %s″' -- use DD° MM′ SS″ format
c = 3600
elseif degPrec<0.5 then -- 0.0083<degPrec<0.5
formatStr = '%s° %s′' -- use DD° MM′ format
c = 60
else -- if degPrec>0.5 then
formatStr = '%s°' -- use DD° format
c = 1
end
end
-- create degree, minute and seconds numbers and string
d = c/60
k = math.floor(c*(degree%360)+0.49) -- convert float to an integer. This step HAS to be identical for all conversions to avoid incorrect results due to different rounding
dNum = math.floor(k/c) % 360 -- degree number (integer in 0-360 range)
mNum = math.floor(k/d) % 60 -- minute number (integer in 0-60 range)
sNum = 3600*(k%d) / c -- seconds number (float in 0-60 range with 0, 1 or 2 decimal digits)
dStr = Lang:formatNum(dNum) -- degree string
mStr = Lang:formatNum(mNum) -- minute string
sStr = Lang:formatNum(sNum) -- second string
zero = Lang:formatNum(0) -- zero string in local language
if mNum<10 then
mStr = zero .. mStr -- pad with zero if a single digit
if long_m >= 60 then
end
end
if long_m < 0 then
if sNum<10 then
sStr = zero .. sStr -- pad with zero if less than ten
end
end
return string.format(formatStr, dStr, mStr, sStr);
end
end
--[[
--[[============================================================================
Convert degrees to degrees/minutes/seconds notation commonly used when displaying
coordinates.
Inputs:
1) latitude or longitude angle in degrees
2) georeference precision in degrees
3) language used in formatting of the number
==============================================================================]]
function p.deg2dms(frame)
local args = getArgs(frame)
local degree = tonumber(args[1])
local degPrec = tonumber(args[2]) or 0-- precision in degrees
if degree==nil then
return args[1];
else
return p._deg2dms(degree, degPrec, args.lang)
return nil, {{"parseDec", "Missing longitude"}}
return nil, {{"parseDec", "Longitude could not be parsed as a number: " .. long}}
end
end
end
--[[============================================================================
Format coordinate location string, by creating and joining DMS strings for
latitude and longitude. Also convert precision from meters to degrees.
INPUTS:
if format then
* lat = latitude in degrees
* lon = longitude in degrees
* lang = language code
* prec = geolocation precision in meters
==============================================================================]]
function p._lat_lon(lat, lon, prec, lang)
lat = tonumber(lat)
lon = tonumber(lon)
prec = math.abs(tonumber(prec) or 0)
if lon then -- get longitude to be in -180 to 180 range
lon=lon%360
if lon>180 then
lon = lon-360
end
end
if lat==nil or lon==nil then
return NoLatLonString
else
else
local nsew = langSwitch(i18n.NSEW, lang) -- find set of localized translation of N, S, W and E in the desired language
local SN, EW, latStr, lonStr, lon2m, lat2m, phi
if lat<0 then SN = nsew.S else SN = nsew.N end -- choose S or N depending on latitude degree sign
if lon<0 then EW = nsew.W else EW = nsew.E end -- choose W or E depending on longitude degree sign
lat2m=1
lon2m=1
if prec>0 then -- if user specified the precision of the geo location...
phi = math.abs(lat)*math.pi/180 -- latitude in radiants
lon2m = 6378137*math.cos(phi)*math.pi/180 -- see https://en.wikipedia.org/wiki/Longitude
lat2m = 111000 -- average latitude degree size in meters
end
latStr = p._deg2dms(math.abs(lat), prec/lat2m, lang) -- Convert latitude degrees to degrees/minutes/seconds
lonStr = p._deg2dms(math.abs(lon), prec/lon2m, lang) -- Convert longitude degrees to degrees/minutes/seconds
return string.format('%s %s, %s %s', latStr, SN, lonStr, EW)
--return string.format('<span class="latitude">%s %s</span>, <span class="longitude">%s %s</span>', latStr, SN, lonStr, EW)
end
end
end
return coordinateSpec, errors
function p.lat_lon(frame)
local args = getArgs(frame)
return p._lat_lon(args.lat, args.lon, args.prec, args.lang)
end
end
--[[
--[[============================================================================
Helper core function for externalLink. Create URL for different sites:
INPUTS:
* site = Possible sites: GeoHack, GoogleEarth, Proximityrama,
OpenStreetMap, GoogleMaps (for Earth, Mars and Moon)
* globe = Possible options: Earth, Mars or Moon. Venus, Mercury, Titan,
Ganymede are also supported but are unused as of 2013.
* latStr = latitude string or number
* lonStr = longitude string or number
* lang = language code
* attributes = attributes to be passed to GeoHack
==============================================================================]]
function p._externalLink(site, globe, latStr, lonStr, lang, attributes, level)
local URLstr = SiteURL[site];
level = level or 1
local pageName = mw.uri.encode( mw.title.getCurrentTitle().prefixedText, 'WIKI' )
pageName = mw.ustring.gsub( pageName, '%%', '%%%%')
if site == 'GoogleMaps' then
URLstr = SiteURL.GoogleMaps[globe]
elseif site == 'GeoHack' then
attributes = string.format('globe:%s_%s', globe, attributes)
URLstr = mw.ustring.gsub( URLstr, '$attr', attributes)
end
end
URLstr = mw.ustring.gsub( URLstr, '$lat' , latStr)
URLstr = mw.ustring.gsub( URLstr, '$lon' , lonStr)
URLstr = mw.ustring.gsub( URLstr, '$lang' , lang)
URLstr = mw.ustring.gsub( URLstr, '$level', level)
URLstr = mw.ustring.gsub( URLstr, '$page' , pageName)
URLstr = mw.ustring.gsub( URLstr, '+', '')
URLstr = mw.ustring.gsub( URLstr, ' ', '_')
return URLstr
end
--[[============================================================================
Create URL for different sites.
INPUTS:
* site = Possible sites: GeoHack, GoogleEarth, Proximityrama,
OpenStreetMap, GoogleMaps (for Earth, Mars and Moon)
* globe = Possible options: Earth, Mars or Moon. Venus, Mercury, Titan,
return coordinateSpec, errors, backward
Ganymede are also supported but are unused as of 2013.
* lat = latitude string or number
* lon = longitude string or number
* lang = language code
* attributes = attributes to be passed to GeoHack
==============================================================================]]
function p.externalLink(frame)
local args = getArgs(frame)
return p._externalLink(args.site or 'GeoHack', args.globe or 'Earth', args.lat, args.lon, args.lang, args.attributes or '')
end
end
--[[
--[[============================================================================
Adjust GeoHack attributes depending on the template that calls it
INPUTS:
* attributes = attributes to be passed to GeoHack
* mode = set by each calling template
==============================================================================]]
function p.alterAttributes(attributes, mode)
-- indicate which template called it
if mode=='camera' then -- Used by {{Location}} and {{Location dec}}
if string.find(attributes, 'type:camera')==nil then
attributes = 'type:camera_' .. attributes
if not args[1] then
if not result then
end
end
if not result then
elseif mode=='object'or mode =='globe' then -- Used by {{Object location}}
if mode=='object' and string.find(attributes, 'type:')==nil then
attributes = 'type:object_' .. attributes
end
end
if string.find(attributes, 'class:object')==nil then
attributes = 'class:object_' .. attributes
end
end
elseif mode=='inline' then -- Used by {{Inline coordinates}} (actually that template does not set any attributes at the moment)
elseif mode=='user' then -- Used by {{User location}}
-- Error
attributes = 'type:user_location'
elseif mode=='institution' then --Used by {{Institution/coordinates}} (categories only)
attributes = 'type:institution'
end
end
return attributes
end
--[[============================================================================
Create link to GeoHack tool which displays latitude and longitude coordinates
in DMS format
INPUTS:
* globe = Possible options: Earth, Mars or Moon. Venus, Mercury, Titan,
Ganymede are also supported but are unused as of 2013.
* lat = latitude in degrees
* lon = longitude in degrees
* lang = language code
* prec = geolocation precision in meters
* attributes = attributes to be passed to GeoHack
==============================================================================]]
function p._GeoHack_link(args)
-- create link and coordintate string
local latlon = p._lat_lon(args.lat, args.lon, args.prec, args.lang)
if latlon==NoLatLonString then
return latlon
else
local url = p._externalLink('GeoHack', args.globe or 'Earth', args.lat, args.lon, args.lang, args.attributes or '')
return string.format('<span class="plainlinksneverexpand">[%s %s]</span>', url, latlon) --<span class="plainlinks nourlexpansion">
end
end
end
end
function p.GeoHack_link(frame)
return p._GeoHack_link(getArgs(frame))
end
end
--[[============================================================================
Create full external links section of {{Location}} or {{Object location}}
templates, based on:
* globe = Possible options: Earth, Mars or Moon. Venus, Mercury, Titan, Ganymede are also supported but are unused as of 2013.
* mode = Possible options:
- camera - call from {{location}}
- object - call from {{Object location}}
- globe - call from {{Globe location}}
* lat = latitude in degrees
* lon = longitude in degrees
* lang = language code
* namespace = namespace name: File, Category, (Gallery)
==============================================================================]]
function p._externalLinksSection(args)
local lang = args.lang
if not args.namespace then
args.namespace = mw.title.getCurrentTitle().namespace
end
local str, link1, link2, link3, link4
if args.globe=='Earth' and args.namespace~="Category" then -- Earth locations for files will have 2 links
link1 = p._externalLink('OpenStreetMap1', 'Earth', args.lat, args.lon, lang, '')
link2 = p._externalLink('GoogleEarth' , 'Earth', args.lat, args.lon, lang, '')
str = string.format('[%s %s] - [%s %s]',
link1, langSwitch(i18n.OpenStreetMaps, lang),
link2, langSwitch(i18n.GoogleEarth, lang))
elseif args.globe=='Earth' and args.namespace=="Category" then -- Earth locations for categories will have 4 links
link1 = p._externalLink('OpenStreetMap2', 'Earth', args.lat, args.lon, lang, '', args.catRecurse)
--link2 = p._externalLink('GoogleMaps' , 'Earth', args.lat, args.lon, lang, '', args.catRecurse)
link3 = p._externalLink('GoogleEarth' , 'Earth', args.lat, args.lon, lang, '')
link4 = p._externalLink('Proximityrama' , 'Earth', args.lat, args.lon, lang, '')
str = string.format('[%s %s] - [%s %s] - [%s %s]',
link1, langSwitch(i18n.OpenStreetMaps, lang),
--link2, langSwitch(i18n.GoogleMaps, lang),
link3, langSwitch(i18n.GoogleEarth, lang),
link4, langSwitch(i18n.Proximityrama, lang))
elseif args.globe=='Mars' or args.globe=='Moon' then
link1 = p._externalLink('GoogleMaps', args.globe, args.lat, args.lon, lang, '')
str = string.format('[%s %s]', link1, langSwitch(i18n.GoogleMaps, lang))
end
return str
return coord_link;
end
end
function p.externalLinksSection(frame)
return p._externalLinksSection(getArgs(frame))
end
--[[============================================================================
Core section of template:Location, template:Object location and template:Globe location.
This method requires several arguments to be passed to it or it's parent method/template:
{{#invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
* globe = Possible options: Earth, Mars or Moon. Venus, Mercury, Titan, Ganymede are also supported but are unused as of 2013.
* mode = Possible options:
- camera - call from {{location}}
- object - call from {{Object location}}
- globe - call from {{Globe location}}
* lat = latitude in degrees
* lon = longitude in degrees
* attributes = attributes
* lang = language code
* namespace = namespace: File, Category, Gallery
* prec = geolocation precision in meters
==============================================================================]]
function p._LocationTemplateCore(args)
-- prepare arguments
if not (args.namespace) then -- if namespace not provided than look it up
args.namespace = mw.title.getCurrentTitle().namespace
end
if args.namespace=='' then -- if empty than it is a gallery
args.namespace = 'Gallery'
end
local bare = yesno(args.bare,false)
local Status = 'primary' -- used by {{#coordinates:}}
if yesno(args.secondary,false) then
Status = 'secondary'
end
local attributes0 = args.attributes
args.attributes = p.alterAttributes(args.attributes or '', args.mode)
-- Convert coordinates from string to numbers
local lat = tonumber(args.lat)
local lon = tonumber(args.lon)
to use.
if lon then -- get longitude to be in -180 to 180 range
lon=lon%360
if lon>180 then
lon = lon-360
local coordinate = args[1]
end
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
if precision <= 0 then
end
end
-- If wikidata link provided than compare coordinates
local Categories, geoMicroFormat, coorTag, wikidata_link = '', '', '', ''
if args.wikidata and args.wikidata~='' then
local dist_str, q
-- if lat/lon is not provided but we are given wikidata q-code than look up the coordinates
lat, lon, q, Categories, dist_str = mergeWithWikidata(args.wikidata, lat, lon)
wikidata_link = string.format("\n[[File:Wikidata-logo.svg|20px|Edit coordinates on Wikidata%s|link=wikidata:%s]]", dist_str, q);
end
args.lat = string.format('%010.6f', lat or 0)
args.lon = string.format('%011.6f', lon or 0)
local frame = mw.getCurrentFrame()
-- Categories, {{#coordinates}} and geoMicroFormat will be only added to File, Category and Gallery pages
if (args.namespace == 'File' or args.namespace == 'Category' or args.namespace == 'Gallery') then
if lat and lon then -- if lat and lon are numbers...
{{#invoke:Coordinates | coord }}
if lat==0 and lon==0 then -- lat=0 and lon=0 is a common issue when copying from flickr and other sources
Categories = Categories .. CoorCat.default
end
if attributes0 and string.find(attributes0, '=') then
Categories = Categories .. CoorCat.attribute
end
if args.error=='1' or (math.abs(lat)>90) then -- check for errors ({{#coordinates:}} also checks for errors )
Categories = Categories .. CoorCat.erroneous
end
local cat = CoorCat[args.namespace]
if cat then -- add category based on namespace
Categories = Categories .. cat
end
-- if not earth than add a category for each globe
if args.mode and args.globe and args.mode=='globe' and args.globe~='Earth' then
Categories = Categories .. string.format(CoorCat[args.mode], args.globe)
end
-- add <span class="geo"> Geo (microformat) code: it is included for machine readability
geoMicroFormat = string.format('<span class="geo" style="display:none">%10.6f; %11.6f</span>',lat, lon)
and entity.claims
-- add {{#coordinates}} tag, see https://www.mediawiki.org/wiki/Extension:GeoData
if args.namespace == 'File' and Status == 'primary' and args.mode=='camera' then
coorTag = frame:callParserFunction( '#coordinates', { 'primary', lat, lon, args.attributes } )
elseif args.namespace == 'File' and args.mode=='object' then
coorTag = frame:callParserFunction( '#coordinates', { lat, lon, args.attributes } )
if precision then
end
end
else -- if lat and lon are not numbers then add error category
Categories = Categories .. CoorCat.erroneous
end
end
end
end
local function isInline(s)
-- Call helper functions to render different parts of the template
local coor, info_link, inner_table, heading, OSM = '','','','','','',''
coor = p._GeoHack_link(args) -- the p and link to GeoHack
heading = p._getHeading(attributes0) -- get heading arrow section
if heading then
local k = math.fmod(math.floor(0.5+math.fmod(heading+360,360)/11.25),32)+1
local fname = heading_icon[k]
coor = string.format('%s <span title="%s°">[[%s|25px|link=|alt=Heading=%s°]]</span>', coor, heading, fname, heading)
end
end
if args.globe=='Earth' then
local icon = 'marker'
if args.mode=='camera' then
icon = 'camera'
end
OSM = frame:preprocess(add_maplink(args.lat, args.lon, icon, '[[File:Openstreetmap logo.svg|20px|link=|Kartographer map based on OpenStreetMap.]]')) -- fancy link to OSM
end
end
local external_link = p._externalLinksSection(args) -- external link section
local function coord_wrapper(in_args)
if external_link and args.namespace == 'File' then
-- Calls the parser function {{#coordinates:}}.
external_link = langSwitch(i18n.LocationTemplateLinkLabel, args.lang) .. ' ' .. external_link -- header of the link section for {{location}} template
elseif external_link then
external_link = langSwitch(i18n.ObjectLocationTemplateLinkLabel, args.lang) .. ' ' .. external_link -- header of the link section for {{Object location}} template
end
end
info_link = string.format('[[File:OOjs UI icon help.svg|18x18px|alt=info|link=%s]]', langSwitch(i18n.COM_GEO, args.lang) )
inner_table = string.format('<td style="border:none;">%s %s</td><td style="border:none;">%s</td><td style="border:none;">%s%s%s</td>',
coor, OSM, external_link or '', info_link, wikidata_link, geoMicroFormat)
local text = ''
-- combine strings into a table
if isInline(Display) then
local templateText
if bare then
templateText = string.format('<table style="width:100%%"><tr>%s</tr></table>', inner_table)
else
-- choose name of the field
local field_name = 'Location'
.. makeWikidataCategories(args.qid)
if args.mode=='camera' then
field_name = langSwitch(i18n.CameraLocation, args.lang)
elseif args.mode=='object' then
field_name = langSwitch(i18n.ObjectLocation, args.lang)
elseif args.mode=='globe' then
local tmp = {}
local field_list = langSwitch(i18n.GlobeLocation, args.lang)
if args.globe and i18n.GlobeLocation['en'][args.globe] then -- verify globe is provided and is recognized
field_name = field_list[args.globe]
end
end
end
--Create HTML text
local dir, text_align
if mw.language.new( args.lang ):isRTL() then
dir = 'rtl'
text_align = 'right'
--[[
local result, negative = mw.text.split((mw.ustring.match(frame.args[1],'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')
else
else
dir = 'ltr'
end
text_align = 'left'
end
local style = string.format('class="toccolours mw-content-%s layouttemplate commons-file-information-table" cellpadding="2" style="width: 100%%; direction:%s;" lang="%s"',
args.lang, dir, text_align, args.lang)
templateText = string.format('<table lang="%s" %s><tr><th class="type fileinfo-paramfield">%s</th>%s</tr></table>', args.lang, style, field_name, inner_table)
end
end
return templateText, Categories, coorTag
end
end
function p.LocationTemplateCore(frame)
local args = frame.args
if not args or not args.lat then -- if no arguments provided than use parent arguments
args = mw.getCurrentFrame():getParent().args
function coordinates.coordinsert(frame)
end
end
if frame.args.name then
if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then
args.lang = frame:callParserFunction("int","lang") -- get user's chosen language
end
end
return frame.args[1]
local templateText, Categories, coorTag = p._LocationTemplateCore(args)
return templateText .. Categories .. coorTag
end
end
return coordinates
return p