Module: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.

: Export the link used to reach the tools

require ('strict')

local math_mod = require ("Module:Math")

local coordinates = ;

local isSandbox = mw.getCurrentFrame :getTitle :find ('sandbox', 1, true);

local current_page = mw.title.getCurrentTitle

local pagename = mw.uri.encode (currentpage.prefixedText, 'WIKI');

local coordlink = '//geohack.toolforge.org/geohack.php?language=zh&pagename=' .. pagename .. '&params='

-- Helper function, replacement for

local function displaytitle (coords)

	return mw.getCurrentFrame :extensionTag {

		name = 'indicator',

		args = {name = 'coordinates'},

		content = ' 座標：' .. coords ..

	}

end

-- Helper function, used in detecting DMS formatting

local function dmsTest (first, second)

	if type (first) ~= 'string' or type (second) ~= 'string' then

		return nil

	end

	local s = (first .. second):upper

	return s:find ('^NSEW$') or s:find ('^EWNS$')

end

-- Wrapper function to grab args, see Module:Arguments for this function's documentation.

local function makeInvokeFunc (funcName)

	return function (frame)

		local args = require ('Module:Arguments').getArgs (frame, {wrappers = 'Template:Coord'})

		return coordinatesfuncName (args, frame)

	end

end

-- Helper function, handle optional args.

local function optionalArg (arg, supplement)

	return arg and arg .. supplement or

end

--

Formats any error messages generated for display

local function errorPrinter (errors)

	local result = ""

	for i,v in ipairs (errors) do

		result = result .. ' 座標：' .. v2 ..

	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

local function displayDefault (default, mode)

	if default == "" then

		default = "dec"

	end

	if default == mode then

		return "geo-default"

	else

		return "geo-nondefault"

	end

end

--

specPrinter

Output formatter. Takes the structure generated by either parseDec

or parseDMS and formats it for inclusion on Wikipedia.

local function specPrinter (args, coordinateSpec)

	local uriComponents = coordinateSpec"param"

	if uriComponents == "" then

		-- RETURN error, should never be empty or nil

		return "ERROR 引數為空"

	end

	if args"name" 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

	local geodeclat

	if lat '

			.. geodeclat .. ' '

			.. geodeclong

			..

	local geonumhtml =

			.. coordinateSpec"dec-lat" .. '; '

			.. coordinateSpec"dec-long"

			..

	local inner = .. geodmshtml ..

				.. '﻿ / ﻿'

				.. ;

	if not args"name" then

		inner = inner .. geodechtml

				.. '﻿ / ' .. geonumhtml ..

	else

		inner = inner .. .. geodechtml

				.. '﻿ / ' .. geonumhtml ..

				.. '﻿ ('

				.. args"name" .. ')'

	end

local stylesheetLink = 'Module:Coordinates' .. (isSandbox and '/sandbox' or) .. '/styles.css'

	return mw.getCurrentFrame :extensionTag {name = 'templatestyles', args = { src = stylesheetLink}

	} .. .. coord_link .. uriComponents ..

	' ' .. inner .. ..

end

-- Helper function, convert decimal to degrees

local function convertdec2dmsd (coordinate)

	local d = mathmod.round (coordinate, 0) .. "°"

	return d .. ""

end

-- Helper function, convert decimal to degrees and minutes

local function convertdec2dmsdm (coordinate)

	coordinate = mathmod.round (coordinate * 60, 0);

	local m = coordinate % 60;

	coordinate = math.floor ((coordinate - m) / 60 );

	local d = coordinate % 360 .."°"

	return d .. string.format ("%02d′", m)

end

-- Helper function, convert decimal to degrees, minutes, and seconds

local function convertdec2dmsdms (coordinate)

	coordinate = mathmod.round (coordinate 60 60, 0);

	local s = coordinate % 60

	coordinate = math.floor ((coordinate - s) / 60 );

	local m = coordinate % 60

	coordinate = math.floor ((coordinate - m) / 60 );

	local d = coordinate % 360 .."°"

	return d .. string.format ("%02d′", m) .. string.format ("%02d″", s)

end

--

Helper function, convert decimal latitude or longitude to

degrees, minutes, and seconds format based on the specified precision.

local function convert_dec2dms (coordinate, firstPostfix, secondPostfix, precision)

	local coord = tonumber (coordinate)

	local postfix

	if coord >= 0 then

		postfix = firstPostfix

	else

		postfix = secondPostfix

	end

	precision = precision:lower ;

	if precision == "dms" then

		return convertdec2dmsdms (math.abs ( coord) ) .. postfix;

	elseif precision == "dm" then

		return convertdec2dmsdm (math.abs ( coord) ) .. postfix;

	elseif precision == "d" then

		return convertdec2dmsd (math.abs ( coord) ) .. postfix;

	end

end

--

Convert DMS format into a N or E decimal coordinate

local function convertdms2dec (direction, degreesstr, minutesstr, secondsstr)

	local degrees = tonumber (degrees_str)

	local minutes = tonumber (minutes_str) or 0

	local seconds = tonumber (seconds_str) or 0

	local factor = 1

	if direction == "S" or direction == "W" then

		factor = -1

	end

	local precision = 0

	if seconds_str then

		precision = 5 + math.max (mathmod.precision (seconds_str), 0 );

	elseif minutesstr and minutesstr ~= then

		precision = 3 + math.max (mathmod.precision (minutes_str), 0 );

	else

		precision = math.max (mathmod.precision (degrees_str), 0 );

	end

	local decimal = factor * (degrees+(minutes+seconds/60)/60)

	return string.format ("%." .. precision .. "f", decimal) -- not tonumber since this whole thing is string based.

end

--

Checks input values to for out of range errors.

local function validate (latd, latm, lats, longd, longm, longs, source, strong)

	local errors = ;

	latd = tonumber (latd) or 0;

	latm = tonumber (latm) or 0;

	lats = tonumber (lats) or 0;

	longd = tonumber (longd) or 0;

	longm = tonumber (longm) or 0;

	longs = tonumber (longs) or 0;

	if strong then

		if lat_d 180 then

			table.insert (errors, {source, "longitude degrees> 180 with hemisphere flag"})

		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 longs 0 or mathmod.precision (longd) > 0 then

			if lat_f:upper == 'S' then

				latd = '-' .. latd;

			end

			if long_f:upper == 'W' then

				longd = '-' .. longd;

			end

			return parseDec (latd, longd, format);

		end

	end

	coordinateSpec"dms-lat" = latd.."°"..optionalArg (latm,"′") .. optionalArg (lats,"″") .. latf

	coordinateSpec"dms-long" = longd.."°"..optionalArg (longm,"′") .. optionalArg (longs,"″") .. longf

	coordinateSpec"dec-lat" = convertdms2dec (latf, latd, latm, lat_s) -- }

	coordinateSpec"dec-long" = convertdms2dec (longf, longd, longm, long_s) -- }

	if format then

		coordinateSpec.default = format

	else

		coordinateSpec.default = "dms"

	end

	return coordinateSpec, errors, backward

end

--

Check the input arguments for coord to determine the kind of data being provided

and then make the necessary processing.

local function formatTest (args)

	local result, errors

	local backward, primary = false, false

	local function getParam (args, lim)

		local ret =

		for i = 1, lim do

			reti = argsi or

		end

		return table.concat (ret, '_')

	end

	if not args1 then

		-- no lat logic

		return errorPrinter ( )

	elseif not tonumber (args1) then

		-- bad lat logic

		return errorPrinter ( )

	elseif not args4 and not args5 and not args6 then

		-- dec logic

		result, errors = parseDec (args1, args2, args.format)

		if not result then

			return errorPrinter (errors);

		end

		-- formatting for geohack: geohack expects DND_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 (args1)),

			((tonumber (args1) or 0) 0 then

		ret = ret .. ' ' .. errorPrinter (errors) ..

	end

	return ret, backward

end

--

Generate Wikidata tracking categories.

local function makeWikidataCategories (qid)

	local ret

	local qid = qid or mw.wikibase.getEntityIdForCurrentPage

	if mw.wikibase and current_page.namespace == 0 then

		if qid and mw.wikibase.entityExists (qid) and mw.wikibase.getBestStatements (qid, "P625") and mw.wikibase.getBestStatements (qid, "P625") 1 then

			local snaktype = mw.wikibase.getBestStatements (qid, "P625") 1.mainsnak.snaktype

			if snaktype == 'value' then

				-- coordinates exist both here and on Wikidata, and can be compared.

				ret = ' 維基資料存在座標資料的頁面 '

			elseif snaktype == 'somevalue' then

				ret = ' 維基資料座標資料為未知值的頁面 '

			elseif snaktype == 'novalue' then

				ret = ' 維基資料座標資料為無值的頁面 '

			end

		else

			-- We have to either import the coordinates to Wikidata or remove them here.

			ret = ' 維基資料不存在座標資料的頁面 '

		end

	end

	if ret then

		return string.format (, ret)

	else

		return

	end

end

--

link

Simple function to export the coordinates link for other uses.

Usage:

function coordinates.link (frame)

	return coord_link;

end

--

dec2dms

Wrapper to allow templates to call dec2dms directly.

Usage:

decimalcoordinate is converted to DMS format. If positive, the positivesuffix

is appended (typical N or E), if negative, the negative suffix is appended. The

specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail

to use.

coordinates.dec2dms = makeInvokeFunc ('_dec2dms')

function coordinates._dec2dms (args)

	local coordinate = args1

	local firstPostfix = args2 or

	local secondPostfix = args3 or

	local precision = args4 or

	return convert_dec2dms (coordinate, firstPostfix, secondPostfix, precision)

end

--

Helper function to determine whether to use D, DM, or DMS

format depending on the precision of the decimal input.

function coordinates.determineMode (value1, value2)

	local precision = math.max (mathmod.precision ( value1), mathmod.precision (value2) );

	if precision ' .. contents .. Notes ..

	end

	if isInTitle (Display) then

		-- Add to output since indicator content is invisible to Lua later on

		if not isInline (Display) then

			text = text .. .. contents .. Notes ..

		end

		text = text .. displaytitle (contents .. Notes) .. makeWikidataCategories (args.qid)

	end

	if not args.nosave then

		local page_title, count = mw.title.getCurrentTitle , 1

		if backward then

			local tmp =

			while not string.find ((argscount-1 or), 'EW') do tmpcount = (argscount or); count = count+1 end

			tmp.count = count; count = 2*(count-1)

			while count >= tmp.count do table.insert (tmp, 1, (argscount or)); count = count-1 end

			for i, v in ipairs (tmp) do argsi = v end

		else

			while count ') then

			-- take something that looks like a coord template and add the vcard span with class and fn org class

			local namestr = frame.args.name

			frame.args1 = mw.ustring.gsub (

				frame.args1,

				'(>^>^>^)',

				'%1%2﻿ (' .. namestr .. ')%3'

			)

			-- then find anything from coordinates parameters to the 'end' and attach the title parameter

			frame.args1 = mw.ustring.gsub (

				frame.args1,

				'(&params=^&"%% *) ',

				'%1&title=' .. mw.uri.encode (namestr) .. ' '

			)

		end

	end

	-- replace the existing indicator with a new indicator using the modified content

	frame.args1 = mw.ustring.gsub (

		frame.args1,

		'((.+))\127^\127UNIQ%-%-indicator%-% x+%-%-?QINU^\127\127',

		function (inline, coord) return inline .. displaytitle (coord) end

	)

	return frame.args1

end

return coordinates