モジュールの解説[表示] [編集] [履歴] [キャッシュを破棄]

このLuaモジュールには、主に座標の計算を行う関数が入っています。

ウィキデータでのバージョン: 2022-10-22 問題なし

使い方

モジュール内呼び出し用関数
toDec( coord, aDir, prec ) 
10進数か16進数の度分秒の座標を10進数に統一します
引数:
  • coord: 座標
  • aDir: 緯度・経度の順番
  • prec: 小数点以下の桁数
返値:
  • dec: 10進数の座標
  • error: エラー番号
  • parts: 度分秒のパートの数、通常は1(既に10進数)。~4
function cd.getDMSString( coord, prec, aDir, plus, minus, aFormat ) 
10進数か度分秒の座標を度分秒に統一します
function cd.getGeoLink( pattern, lat, long, plusLat, plusLong, minusLat, minusLong, prec, aFormat ) 
toDec関数の最適用なしで完全な度分秒座標の変換を行います
function cd.getDecGeoLink( pattern, lat, long, prec ) 
toDec関数の最適用なしで完全な10進数座標の変換を行います
直接呼出し用関数
function cd.dec2dms( frame ) 
10進数か度分秒の座標を度分秒に統一します
function cd.dms2dec( frame ) 
10進数か16進数の度分秒の座標を10進数に統一します
function cd.geoLink( frame ) 
toDec関数の最適用なしで完全な度分秒座標の変換を行います

背景

このモジュールはMapSources拡張機能を完全に置き換えるために開発されました。MapSources_math.phpを再デザインしたものです。

使用状況

このモジュールを使用しているモジュールは以下の通りです:

--[[ 	Coordinate conversion procedures 	This module is intended to replace the functionality of MapSources extension 	Redesign of my own MapSources_math.php 	Designed for use both in modules and for direct invoking  	Functions for use in modules:  	toDec( coord, aDir, prec ) 		returns a decimal coordinate from decimal or deg-min-sec-letter strings 	getDMSString( coord, prec, aDir, plus, minus, aFormat ) 		formats a decimal/dms coordinate to a deg-min-sec-letter string 	getGeoLink( pattern, lat, long, plusLat, plusLong, minusLat, minusLong, prec, aFormat ) 		converts a complete dms geographic coordinate without reapplying the toDec function 	getDecGeoLink( pattern, lat, long, prec ) 		converts a complete decimal geographic coordinate without reapplying the toDec function  	Invokable functions:  	dec2dms( frame ) 	dms2dec( frame ) 	geoLink( frame )  	Additional functions in Module:GeoData ]]--  -- module import -- require( 'strict' ) local ci = require( 'Module:Coordinates/i18n' )  -- module variable and administration local cd = { 	moduleInterface = { 		suite  = 'Coordinates', 		serial = '2022-10-22', 		item   = 7348344 	} }  -- helper function getErrorMsg -- returns error message by error number which local function getErrorMsg( which ) 	if which == 'noError' or which == 0 then 		return ci.errorMsg.noError 	elseif which > #ci.errorMsg then 		return ci.errorMsg.unknown 	else 		return ci.errorMsg[ which ] 	end end  -- helper function round -- num: value to round -- idp: number of digits after the decimal point local function round( n, idp ) 	local m = 10^( idp or 0 ) 	if n >= 0 then 		return math.floor( n * m + 0.5 ) / m 	else 		return math.ceil( n * m - 0.5 ) / m 	end end  -- helper function getPrecision -- returns integer precision number -- possible values: numbers, D, DM, DMS -- default result: 4 local function getPrecision( prec ) 	local p = tonumber( prec ) 	if p then 		p = round( p, 0 ) 		if p < -1 then 			p = -1 		elseif p > 8 then -- maximum 8 decimals 			p = 8 		end 		return p 	else 		p = prec and prec:upper() or 'DMS' 		if p == 'D' then 			return 0 		elseif p == 'DM' then 			return 2 		else 			return 4 -- DMS = default 		end 	end end  -- helper function toDMS -- splits a decimal coordinate dec to degree, minute and second depending on the -- precision. prec <= 0 means only degree, prec < 3 degree and minute, and so on -- returns a result array local function toDMS( dec, prec ) 	local result = { dec = 0, deg = 0, min = 0, sec = 0, sign = 1, 		NS = 'N', EW = 'E', prec = getPrecision( prec ) } 	local p = result.prec 	 	result.dec = round( dec, 8 ) 	if result.dec < 0 then  		result.sign = -1 		result.NS = 'S' 		result.EW = 'W' 	end  	local angle = math.abs( round( result.dec, p ) ) 	result.deg = math.floor( angle ) 	result.min = ( angle - result.deg ) * 60  	if p > 4 then 		result.sec = round( ( result.min - math.floor( result.min ) ) * 60, p - 4 ) 	else 		result.sec = round( ( result.min - math.floor( result.min ) ) * 60 ) 	end 	result.min = math.floor( result.min ) 	 	if result.sec >= 60 then 		result.sec = result.sec - 60 		result.min = result.min + 1  	end 	if p < 3 and result.sec >= 30 then 		result.min = result.min + 1 	end 	if p < 3 then 		result.sec = 0 	end 	if result.min >= 60 then 		result.min = result.min - 60 		result.deg = result.deg + 1 	end 	if p < 1 and result.min >= 30 then 		result.deg = result.deg + 1 	end 	if p < 1 then 		result.min = 0 	end  	return result end  -- toDec converts decimal and hexagesimal DMS formatted coordinates to decimal -- coordinates -- input --  dec: coordinate --  prec: number of digits after the decimal point --  aDir: lat/long directions -- returns a result array -- output --  dec: decimal value --  error: error number --  parts: number of DMS parts, usually 1 (already decimal) ... 4 function cd.toDec( coord, aDir, prec ) 	local result = { dec = 0, error = 0, parts = 1 }  	local s = mw.text.trim( coord ) 	if s == '' then 		result.error = 1 		return result 	end 	 	-- pretest if already a decimal 	local dir = aDir or '' 	local mx = dir == 'lat' and 90 or 180 	local r = tonumber( s ) 	if r then 		if r < -mx or r > mx or r <= -180 then 			result.error = 5 			return result 		end 		result.dec = round( r, getPrecision ( prec ) ) 		return result 	end  	s = mw.ustring.gsub( s, '[‘’′´`]', "'" ) 	s = s:gsub( "''", '"' ) 	s = mw.ustring.gsub( s, '[“”″]', '"' ) 	s = mw.ustring.gsub( s, '[−–—]', '-' ) 	s = mw.ustring.upper( mw.ustring.gsub( s, '[_/%c%s%z]', ' ' ) ) 	local mStr = '^[ %.%-°\'"0-9' -- string to match, illegal characters? 	for key, value in pairs( ci.inputLetters ) do 		mStr = mStr .. key 	end 	mStr = mStr .. ']+$' 	if not mw.ustring.match( s, mStr ) then 		result.error = 3 		return result 	end	 	s = mw.ustring.gsub( s, '(%u)', ' %1' ) 	s = mw.ustring.gsub( s, '%s*([°"\'])', '%1 ' ) 	s = mw.text.split( s, '%s' ) 	for i = #s, 1, -1 do 		if mw.text.trim( s[ i ] ) == '' then 			table.remove( s, i ) 		end 	end 	result.parts = #s  	if #s < 1 or #s > 4 then 		result.error = 2 		return result 	end  	local units = { '°', "'", '"', ' ' } 	local res   = { 0, 0, 0, 1 } -- 1 = positive direction  	local v 	local l 	for i = 1, #s, 1 do 		v = mw.ustring.gsub( s[ i ], units[ i ], '' )  		if tonumber( v ) then 			if i > 3 then -- this position is for direction letter, not for number 				result.error = 4 				return result 			end  			v = tonumber( v ) 			if i == 1 then 				if v < -mx or v > mx then 					result.error = 5 					return result 				end 				res[ 1 ] = v 			elseif i == 2 or i == 3 then 				if v < 0 or v >= 60 then 					result.error = 2 + 2 * i 					return result 				end 				if res[ i - 1 ] ~= round( res[ i - 1 ], 0 ) then 					result.error = 3 + 2 * i 					return result 				end 				res[ i ] = v 			end 		else -- no number 			if i ~= #s then -- allowed only at the last position 				result.error = 10 				return result 			end 			if res[ 1 ] < 0 then 				result.error = 11 				return result 			end 			l = ci.inputLetters[ v ] 			if mw.ustring.len( v ) ~= 1 or not l then 				result.error = 3 				return result 			end  			-- l[1]: factor 			-- l[2]: lat/long 			if ( dir == 'long' and l[ 2 ] ~= 'long' ) or 				( dir == 'lat' and l[ 2 ] ~= 'lat' ) then 				result.error = 12 				return result 			else 				dir = l[ 2 ] 			end  			res[ 4 ] = l[ 1 ] 		end 	end  	if res[ 1 ] >= 0 then 		result.dec = ( res[ 1 ] + res[ 2 ] / 60 + res[ 3 ] / 3600 ) * res[ 4 ] 	else 		result.dec = ( res[ 1 ] - res[ 2 ] / 60 - res[ 3 ] / 3600 ) * res[ 4 ] 	end  	result.dec = round( result.dec, getPrecision ( prec ) ) 	if result.dec < -mx or result.dec > mx or result.dec <= -180 then 		result.error = 5 		return result 	end  	return result end  -- getDMSString formats a degree-minute-second string for output in accordance -- to a given format specification -- input --  coord: decimal or hexagesimal DMS coordinate --  prec: precion of the coorninate string: D, DM, DMS --  aDir: lat/long direction to add correct direction letters --  plus: alternative direction string for positive directions --  minus: alternative direction string for negative directions --  aFormat: format array with delimiter and leadZeros values or a predefined --  dmsFormats key. Default format key is f1. -- outputs 3 results --  1: formatted string or error message for display --  2: decimal coordinate --  3: absolute decimal coordinate including the direction letter like 51.2323_N function cd.getDMSString( coord, prec, aDir, aPlus, aMinus, aFormat ) 	local d = aDir or '' 	local p = aPlus or '' 	local m = aMinus or ''  	-- format 	local f = aFormat or 'f1' 	if type( f ) ~= 'table' then 		f = ci.dmsFormats[ f ] 	end 	local del = f.delimiter or ' ' 	local lz = f.leadZeros or false  	local c = { dec = tonumber( coord ), error = 0, parts = 1 }  	if not c.dec then 		c = cd.toDec( coord, d, 8 ) 	elseif c.dec <= -180 or c.dec > 180 then 		c.error = 5 	elseif d == 'lat' and ( c.dec < -90 or c.dec > 90 ) then 		c.error = 5  	end  	local l = '' 	local wp = '' 	local result = '' 	if c.error == 0 then 		local dms = toDMS( c.dec, prec ) 		if dms.dec < 0 and d == '' and m == '' then 			dms.deg = -dms.deg 		end  		if lz and dms.min < 10 then 			dms.min = '0' .. dms.min 		end 		if lz and dms.sec < 10 then 			dms.sec = '0' .. dms.sec 		end 		result = dms.deg .. '°' 		if dms.prec > 0 then 			result = result .. del .. dms.min .. '′' 		end 		if dms.prec > 2 and dms.prec < 5 then 			result = result .. del .. dms.sec .. '″' 		end 		if dms.prec > 4 then  			-- enforce sec decimal digits even if zero 			local s = string.format( "%." .. dms.prec - 4 .. "f″", dms.sec ) 			if ci.decimalPoint ~= '.' then  				s = mw.ustring.gsub( s, '%.', ci.decimalPoint ) 			end 			result = result .. del .. s 		end 		 		if d == 'lat' then 			wp = dms.NS 		elseif d == 'long' then 			wp = dms.EW 		end 		if dms.dec >= 0 and p ~= '' then 			l = p 		elseif dms.dec < 0 and m ~= '' then 			l = m 		else 			l = ci.outputLetters[ wp ] 		end  		if l and l ~= '' then 			result = result .. del .. l 		end 		if c.parts > 1 then 			result = result .. ci.categories.dms 		end 		 		return result, dms.dec, math.abs( dms.dec ) .. '_' .. wp 	else 		if d == 'lat' then 			wp = 'N' 		elseif d == 'long' then 			wp = 'E' 		end 		result = '<span class="error" title="' .. getErrorMsg( c.error ) ..'">' 			.. ci.errorMsg.faulty .. '</span>' .. ci.categories.faulty 		return result, '0', '0_' .. wp 	end 	return result	 end  -- getGeoLink returns complete dms geographic coordinate without reapplying the toDec -- and toDMS functions. Pattern can contain placeholders $1 ... $6 --  $1: latitude in Wikipedia syntax including the direction letter like 51.2323_N --  $2: longitude in Wikipedia syntax including the direction letter like 51.2323_E --  $3: latitude in degree, minute and second format considering the strings for --      the cardinal directions and the precision --  $4: longitude in degree, minute and second format considering the strings --      for the cardinal directions and the precision --  $5: latitude --  $6: longitude -- aFormat: format array with delimiter and leadZeros values or a predefined -- dmsFormats key. Default format key is f1. -- outputs 3 results --  1: formatted string or error message for display --  2: decimal latitude --  3: decimal longitude function cd.getGeoLink( pattern, lat, long, plusLat, plusLong, minusLat, 	minusLong, prec, aFormat ) 	local lat_s, lat_dec, lat_wp = 		cd.getDMSString( lat, prec, 'lat', plusLat, minusLat, aFormat ) 	local long_s, long_dec, long_wp = 		cd.getDMSString( long, prec, 'long', plusLong, minusLong, aFormat ) 		 	local s = pattern 	s = mw.ustring.gsub( s, '($1)', lat_wp ) 	s = mw.ustring.gsub( s, '($2)', long_wp ) 	s = mw.ustring.gsub( s, '($3)', lat_s ) 	s = mw.ustring.gsub( s, '($4)', long_s ) 	s = mw.ustring.gsub( s, '($5)', lat_dec ) 	s = mw.ustring.gsub( s, '($6)', long_dec ) 	 	return s, lat_dec, long_dec end  -- getDecGeoLink returns complete decimal geographic coordinate without reapplying -- the toDec function. Pattern can contain placeholders $1 ... $4 function cd.getDecGeoLink( pattern, lat, long, prec )  	local function getDec( coord, prec, aDir, aPlus, aMinus ) 		local l = aPlus 		local c = cd.toDec( coord, aDir, 8 ) 		if c.error == 0 then 			if c.dec < 0 then 				l = aMinus 			end 			local d = round( c.dec, prec ) .. '' 			if ci.decimalPoint ~= '.' then  				d = mw.ustring.gsub( d, '%.', ci.decimalPoint ) 			end 			return d, math.abs( c.dec ) .. '_' .. l 		else 			c.dec = '<span class="error" title="' .. getErrorMsg( c.error ) ..'">' 				.. ci.errorMsg.faulty .. '</span>' .. ci.categories.faulty 			return c.dec, '0_' .. l 		end 	end  	local lat_dec, lat_wp = getDec( lat, prec, 'lat', 'N', 'S' ) 	local long_dec, long_wp = getDec( long, prec, 'long', 'E', 'W' )  	local s = pattern 	s = mw.ustring.gsub( s, '($1)', lat_wp) 	s = mw.ustring.gsub( s, '($2)', long_wp) 	s = mw.ustring.gsub( s, '($3)', lat_dec) 	s = mw.ustring.gsub( s, '($4)', long_dec)  	return s, lat_dec, long_dec end  -- Invokable functions  -- identical to MapSources #dd2dms tag -- frame input --  1 or coord: decimal or hexagesimal coordinate --  precision: precion of the coorninate string: D, DM, DMS --  plus: alternative direction string for positive directions --  minus: alternative direction string for negative directions --  format: Predefined dmsFormats key. Default format key is f1. function cd.dec2dms( frame ) 	local args     = frame:getParent().args 	args.coord     = args[ 1 ] or args.coord or '' 	args.precision = args.precision or ''  	local r, dec, absol = cd.getDMSString( args.coord, args.precision, '', 		args.plus, args.minus, args.format ) 	return r end  -- identical to MapSources #deg2dd tag function cd.dms2dec( frame ) 	local args     = frame:getParent().args 	args.coord     = args[ 1 ] or args.coord or '' 	args.precision = args.precision or ''  	local r = cd.toDec( args.coord, '', args.precision ) 	local s = r.dec 	if r.error ~= 0 then 		s = '<span class="error" title="' .. getErrorMsg( r.error ) ..'">' 			.. ci.errorMsg.faulty .. '</span>' .. ci.categories.faulty 	end 	return s end  -- identical to MapSources #geoLink tag -- This function can be extended to add Extension:GeoData #coordinates because -- cd.getGeoLink returns lat and long, too function cd.geoLink( frame ) 	local args   = frame:getParent().args 	args.pattern = args[ 1 ] or args.pattern or '' 	if args.pattern == '' then 		return errorMsg[ 14 ] 	end  	return cd.getGeoLink( args.pattern, args.lat, args.long, 		args.plusLat, args.plusLong, args.minusLat, args.minusLong, 		args.precision, args.format ) end  return cd