モジュール:サンドボックス/Ef3/Coordinates

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

: General function formatting and displaying
coordinate values.

: Simple function for converting decimal
degree values to DMS format.

: Simple function for converting DMS format
to decimal degree format.


math_mod = require( "Module:Math" );
globalFrame = nil

coordinates = {};

-- Helper function, replacement for  
function displaytitle (s, notes)
local l = "座標: " .. s
local co =  .. l .. notes .. ;
return  .. co .. ;
end

-- Helper function, Replacement for  
function displayinline (s, notes)
return s .. notes    
end

-- Helper function, used in detecting DMS formatting 
local dmsTest = function(first, second)
local concatenated = first:upper() .. second:upper();

if concatenated == "NE" or concatenated == "NW" or concatenated == "SE" or concatenated == "SW" or
concatenated == "EN" or concatenated == "WN" or concatenated == "ES" or concatenated == "WS" then
return true;
end
return false;
end

--
parseDec

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

if long == "" or long == nil then
return nil, 
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, "北緯", "南緯", mode)  -- 
coordinateSpec"dms-long" = convert_dec2dms( long, "東経", "西経", mode)  --     

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

return coordinateSpec, errors
end

-- Helper function, handle optional args. 
function optionalArg(arg, suplement)
if arg ~= nil and arg ~= "" then
return arg .. suplement
end
return ""
end

--
parseDMS

Transforms degrees, minutes, seconds format latitude and longitude 
into the a structure to be used in displaying coordinates
function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
local coordinateSpec = {}
local errors = {}
local format_en2ja = {
N = "北緯",
S = "南緯",
E = "東経",
W = "西経"
}

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

-- Check if specified backward
if lat_f == 'E' or lat_f == 'W' then
local t_d, t_m, t_s, t_f;
t_d = lat_d;
t_m = lat_m;
t_s = lat_s;
t_f = lat_f;
lat_d = long_d;
lat_m = long_m;
lat_s = long_s;
lat_f = long_f;
long_d = t_d;
long_m = t_m;
long_s = t_s;
long_f = t_f;
end    

errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
if long_d == nil or long_d == "" then
table.insert(errors, {"parseDMS", "経度が指定されていません" })
end

if lat_m == nil and lat_s == nil and long_m == nil and long_s == nil 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"  = format_en2jalat_f .. lat_d.."度"..optionalArg(lat_m,"分") .. optionalArg(lat_s,"秒")
coordinateSpec"dms-long" = format_en2jalong_f .. long_d.."度"..optionalArg(long_m,"分") .. optionalArg(long_s,"秒")
coordinateSpec"dec-lat"  = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- 
coordinateSpec"dec-long" = convert_dms2dec(long_f, long_d, long_m, long_s) -- 

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

return coordinateSpec, errors
end

--
specPrinter

Output formatter.  Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
function specPrinter(args, coordinateSpec)
local uriComponents = coordinateSpec"param"
if uriComponents == "" then
-- RETURN error, should never be empty or nil
return "エラー: param が空です"
end
if args"name" ~= "" and args"name" ~= nil then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec"name")
end

local geodmshtml = 
..  .. coordinateSpec"dms-lat" .. ' '
..  ..coordinateSpec"dms-long" .. 
.. 

local lat = tonumber( coordinateSpec"dec-lat" ) or 0
if lat '
..  .. geodeclat .. ' '
..  .. geodeclong .. 
.. 

local geonumhtml = 
.. coordinateSpec"dec-lat" .. '; '
.. coordinateSpec"dec-long"
.. 

local inner;
inner =  .. geodmshtml .. 
.. '﻿ / ﻿'
.. ;

if args"name" == "" or args"name" == nil then
inner = inner .. geodechtml 
.. '﻿ / ' .. geonumhtml .. 
else
inner = inner ..  .. geodechtml 
.. '﻿ / ' .. geonumhtml .. 
.. '﻿ ('
.. args"name" .. ')'
end

return  .. globalFrame:preprocess(
'//tools.wmflabs.org/geohack/geohack.php?language=ja&pagename=&params=' ..
uriComponents .. ' ' .. inner .. ) .. 
end

--
Formats any error messages generated for display
function errorPrinter(errors)
local result = ""
for i,v in ipairs(errors) do
local errorHTML = '座標: ' .. v2 .. 
result = result .. errorHTML .. ""
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  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 input arguments for coord to determine the kind of data being provided
and then make the necessary processing.
function formatTest(args)
local result, errors;
local primary = false;

if args1 == "" then
-- no lat logic
return errorPrinter(  )
elseif args4 == "" and args5 == "" and args6 == "" then
-- dec logic
result, errors = parseDec( args1, args2, args'format' )
if result == nil then
return errorPrinter( errors );
end              
result.param    = table.concat( {args1, "_N_", args2, "_E_", args3 } );
elseif dmsTest(args4, args8) then
-- dms logic
result, errors = parseDMS( args1, args2, args3, args4, 
args5, args6, args7, args8, args'format' )
result.param = table.concat( { args1, args2, args3, args4, args5,
args6, args7, args8, args9 } , '_' );
if args10 ~=  then
table.insert( errors, { 'formatTest', '座標オプションが認識できません' } );
end        
elseif dmsTest(args3, args6) then
-- dm logic
result, errors = parseDMS( args1, args2, nil, args3, 
args4, args5, nil, args6, args'format' )
result.param = table.concat( { args1, args2, args3, args4, args5,
args6, args7 } , '_' );
if args8 ~=  then
table.insert( errors, { 'formatTest', '座標オプションが認識できません' } );
end        
elseif dmsTest(args2, args4) then
-- d logic
result, errors = parseDMS( args1, nil, nil, args2, 
args3, nil, nil, args4, args'format' )
result.param = table.concat( { args1, args2, args3, args4, args5 } , '_' );
if args6 ~=  then
table.insert( errors, { 'formatTest', '座標オプションが認識できません' } );
end        
else
-- Error
return errorPrinter(  )
end
result.name     = args"name"

local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
for _, v in ipairs( extra_param ) do
if (argsv or ) ~=  then 
table.insert( errors, {'formatTest', '引数: "' .. v .. '=" は "' .. v .. ':" という形式でなければいけません' } );
end
end

if #errors == 0 then
return specPrinter( args, result )    
else
return specPrinter( args, result ) .. " " .. errorPrinter(errors) .. ; 
end    
end

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

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

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

-- Helper function, convert decimal to degrees and minutes 
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 
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

--
Convert DMS format into a N or E decimal coordinate
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

local factor
direction = mw.ustring.gsub(direction, '^ *(.-) *$', '%1');
if direction == "N" or direction == "E" then
factor = 1
else
factor = -1
end

local precision = 0
if seconds_str ~= nil and seconds_str ~=  then
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
elseif minutes_str ~= nil 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.
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  180 then
table.insert(errors, {source, "経度が180度を上回っていますが東西が指定されています"})
end

end    

if lat_d > 90 then
table.insert(errors, {source, "緯度の度が90を上回っています"})
end
if lat_d = 60 then
table.insert(errors, {source, "緯度の分が60以上です"})
end
if lat_m = 60 then
table.insert(errors, {source, "緯度の秒が60以上です"})
end
if lat_s = 360 then
table.insert(errors, {source, "経度の度が360以上です"})
end
if long_d = 60 then
table.insert(errors, {source, "経度の分が60以上です"})
end
if long_m = 60 then
table.insert(errors, {source, "経度の秒が60以上です"})
end
if long_s < 0 then
table.insert(errors, {source, "経度の秒が0を下回っています"})
end

return errors;
end

--
dec2dms

Wrapper to allow templates to call dec2dms directly.

Usage:


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.
function coordinates.dec2dms(frame)
globalFrame = frame
local coordinate = frame.args1
local firstPostfix = frame.args2
local secondPostfix = frame.args3
local precision = frame.args4

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:


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).
function coordinates.dms2dec(frame)
globalFrame = frame
local direction = frame.args1
local degrees = frame.args2
local minutes = frame.args3
local seconds = frame.args4

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

--
coord

Main entry point for Lua function to replace 

Usage:
coord 
long 
long_flag 
...

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

Note: This function provides the visual display elements of .  In
order to load coordinates into the database, the  parser 
function must also be called, this is done automatically in the Lua
version of .
function coordinates.coord(frame)
globalFrame = frame
local args = frame.args
if args1 == nil then
local pFrame = frame:getParent();
args = pFrame.args;
for k,v in pairs( frame.args ) do
argsk = v;
end
end

for i=1,10 do 
if argsi == nil then 
argsi = ""
else
argsi = argsi:match( '^%s*(.-)%s*$' );  --remove whitespace
end        
end
args'format' = args'format' or ;

local contents = formatTest(args)
local Notes = args.notes or ""
local Display = string.lower(args.display or "inline")
if Display ==  then
Display = 'inline';
end

local text = 
if string.find( Display, 'inline' ) ~= nil or Display == 'i' or 
Display == 'it' or Display == 'ti' then
text = displayinline(contents, Notes)
end
if string.find( Display, 'title' ) ~= nil or Display == 't' or 
Display == 'it' or Display == 'ti' then
text = text .. displaytitle(contents, Notes)
end
return text
end

return coordinates