Módulo:Coordenadas

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Saltar para a navegação Saltar para a pesquisa

A documentação para este módulo pode ser criada na página Módulo:Coordenadas/doc

local math_mod = require( "Módulo:Math" )
local p = {}
local current_page = mw.title.getCurrentTitle()local page_name = mw.uri.encode( current_page.prefixedText, 'WIKI' );
local coord_link = '//tools.wmflabs.org/geohack/geohack.php?pagename=' .. page_name .. '&params='
--Carregando a lista em /Au/Aux/A
local gdata
local success, resultado = pcall (mw.loadData, "Módulo:Bandeira/Dados" )
if success then
				gdata = resultado
else
				-- Banco de dados minimo em caso de bug em Módulo:Língua/Dados
				gdata={}
				gdata.data={};
				gdata.data[142]={qid="Q45", label="Portugal", genre="ms"}
end
local i18n = {
				N = 'N',
				Nlong = 'norte',
				W = 'O',
				Wlong = 'oeste',
				E = 'L',
				Elong = 'leste',
				S = 'S',
				Slong = 'sul',
				degrees = '° ',
				minutes = '′ ',
				seconds = '″ ',
				geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=pt',
				tooltip = 'Mapas, vistas aéreas, etc.',
				errorcat = '!Páginas com as etiquetas de coordenadas malformadas',
				sameaswikidata = '!Páginas com coordenadas similares no Wikidata',
				notaswikidata = '!Páginas com coordenadas diferentes no Wikidata',
				nowikidata = '!Artigos com coordenadas por transcrever a Wikidata',
				throughwikidata = '!Artigos com coordenadas no Wikidata',
				invalidFormat = 'formato inválido',                        -- 'invalid coordinate format',
				invalidNSEW = 'orientação inválida, deve ser "N", "S", "E" or "W"',       -- 'invalid direction should be "N", "S", "E" or "O"',
				invalidNS = 'orientação de latitude inválida, deve ser "N" ou "S"',       -- 'could not find latitude direction (should be N or S)',
				invalidEW = 'orientação de longitude inválida, deve ser "E" ou "W"',   -- 'could not find longitude direction (should be W or E) ',
				noCardinalDirection = 'orientação cardinal não informada',            -- 'no cardinal direction found in coordinates',
				invalidDirection = 'direção inválida',                      -- 'invalid direction',
				latitude90 = 'latitude > 90',
				longitude360 = 'longitude > 360',
				minSec60 = 'minutos ou segundos > 60',
				negativeCoode = 'em formato dms os graus devem ser positivos',         -- 'dms coordinates should be positive',
				dmIntergers = 'graus e minutos devem ser números inteiros',        -- 'degrees and minutes should be integers',
				tooManyParam = 'muitos parâmetros para latitude ou longitude',     -- 'too many parameters for coordinates',
				coordMissing = 'latitude ou longitude ausente',                -- 'latitude or longitude missing',
				invalidGlobe = 'globo inválido : ',                        -- 'invalid globe:',
}
local coordParse = {
				NORTH = 'N',
				NORTE = 'N',
				EAST = 'E',
				LESTE = 'E',
				L = 'E',
				WEST = 'W',
				W = 'W',
				O = 'W',
				OESTE = 'W',
				SOUTH = 'S',
				SUL = 'S',
}
--Ajuda:Function_genre (gênero)
local genre = {
				ms =    {le="a ",  du="do ",    de="de ",  ao="ao ",  em="em "},
				msa =   {le="a ",   du="da ",  de="de",   ao="à ", em="em "},
				msi =   {le="",     du="do ",    de="de ",  ao="à ",   em="à "},
				msia =  {le="",     du="do ",     de="de ",   ao="à ",   em="à "},
				msiae = {le="",     du="do ",     de="de ",   ao="à ",   em="em "},
				fs =    {le="a ",  du="da ", de="de ",  ao="à ",em="em "},
				fsa =   {le="a ",   du="da ",  de="de ",   ao="à ", em="em "},
				fsi =   {le="",     du="de ",    de="de ",  ao="à ",   em="à "},
				fsia =  {le="",     du="da ",     de="de ",   ao="à ",   em="à "},
				mp =    {le="os ", du="dos ",   de="dos ", ao="aos ", en="em "},
				fp =    {le="as ", du="das ",   de="das ", ao="as ", em="em "}
}
local globedata =     {
				--[[ notes:
								radius in kilometers (especially imprecise for non spheric bodies)
								defaultdisplay is currently disabled, activate it ?
				]]--
				ariel = {radius = 580, defaultdisplay = 'dec east' },
				callisto =  {radius = 2410, defaultdisplay = 'dec west' },
				ceres =  {radius = 470, defaultdisplay = 'dec east' },
				charon =  {radius = 1214, defaultdisplay = 'dec east' },
				deimos =  {radius = 7, defaultdisplay = 'dec west' },
				dione =  {radius = 560, defaultdisplay = 'dec west' },
				enceladus =  {radius = 255, defaultdisplay = 'dec west' },
				ganymede =  {radius = 2634, defaultdisplay = 'dec west' },
				earth = {radius = 6371, defaultdisplay = 'dms' },
				europa =  {radius = 1561, defaultdisplay = 'dec east' },
				hyperion =  {radius = 140, defaultdisplay = 'dec west' },
				iapetus =  {radius = 725, defaultdisplay = 'dec west' },
				['io'] =  {radius = 1322, defaultdisplay = 'dec west' },
				jupiter =  {radius = 68911, defaultdisplay = 'dec east' },
				mars =  {radius = 3389.5, defaultdisplay = 'dec east' },
				mercury =  {radius = 2439.7, defaultdisplay = 'dec west' },
				mimas =  {radius = 197, defaultdisplay = 'dec west' },
				miranda =  {radius = 335, defaultdisplay = 'dec east' },
				moon =  {radius = 1736, defaultdisplay = 'dec' },
				neptune =  {radius = 24553, defaultdisplay = 'dec east' },
				oberon =  {radius = 761, defaultdisplay = 'dec east' },
				phoebe =  {radius = 110, defaultdisplay = 'dec west' },
				phobos =  {radius = 11, defaultdisplay = 'dec west' },
				pluto =  {radius = 1185, defaultdisplay = 'dec east' },
				rhea =  {radius = 765, defaultdisplay = 'dec west' },
				saturn =  {radius = 58232, defaultdisplay = 'dec east' },
				titan =  {radius = 2575.5, defaultdisplay = 'dec west' },
				tethys =  {radius = 530, defaultdisplay = 'dec west' },
				titania =  {radius = 394, defaultdisplay = 'dec east' },
				triton = {radius = 1353, defaultdisplay = 'dec east' },
				umbriel =  {radius = 584, defaultdisplay = 'dec east' },
				uranus =  {radius = 25266, defaultdisplay = 'dec east' },
				venus =  {radius = 6051.8, defaultdisplay = 'dec east' },
				vesta =  {radius = 260, defaultdisplay = 'dec east' }
}
globedata[''] = globedata.earth
local wikidatathreshold = 10 -- Se a distância entre as coordenadas Wikipedia e Wikidata exceder o limite (em quilômetros), uma categoria de manutenção será adicionada
local lang = mw.language.getContentLanguage()
local default_zoom = 13
local function makecat(cat, sortkey)
				if type( sortkey ) == 'string' then
								return '[[Category:' .. cat .. '|' .. sortkey .. ']]'
				else
								return '[[Category:' .. cat .. ']]'
				end
end
----------------------------------------
--Error handling
				--[[ Notes:
				when errors occure a new error message is concatenated to errorstring
				an error message contains an error category with a sortkey
				For major errors, it can also display an error message (the error message will the usually be returned and the function terminated)
				More minor errors do only add a category, so that readers are not bothered with error texts
				sortkeys:
								* A: invalid latitude, longitude or direction
								* B: invalid globe
								* C: something wrong with other parameters
								* D: more than one primary coord
				]]--
local errorstring = ''
local function makeerror(args)
				local errormessage = ''
				if args.message then
								errormessage = '<strong class="error"> Coordenadas : ' .. args.message .. '</strong>'
				end
				local errorcat = ''
				if mw.title.getCurrentTitle().namespace == 0 then
								errorcat = makecat(i18n.errorcat, args.sortkey)
				end
				errorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messages
				return nil
end
local function showerrors()
				return errorstring
end
-- Distance computation
function p._distance(a, b, globe) -- calcula a [[distância orthodromique]] em quilóimetros entre dois pontos do globo
				globe = string.lower(globe or 'earth')
		
				-- check arguments and converts degreees to radians
				local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
				if (not latA) or (not latB) or (not longA) or (not longB) then return
								error('coordenadas não informadas, não consegue calcular a distância')
				end
				if type(latA) ~= 'number' or type(latB) ~= 'number' or type(longA) ~= 'number' or type(longB) ~= 'number' then
								error('coordenadas não são numéricas, não consegue calcular a distância')
				end
								if not globe or not globedata[globe] then
								return error('globe: ' .. globe .. 'não é suportado')
				end
		
				-- calcular a distância angular em radians
				local convratio = math.pi / 180 -- converter em radians
				latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
				local cosangle = math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA)
				if cosangle >= 1 then -- may be above one because of rounding errors
								return 0
				end
				local angle = math.acos(cosangle)
				-- calcular a distância em km
				local radius = globedata[globe].radius
				return radius * angle
end
function p.distance(frame)
				local args = frame.args
				return p._distance(
								{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},
								{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
								args.globe)
end
local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
				extraparams = extraparams or ''
				local geohacklatitude, geohacklongitude
				-- format latitude and longitude for the URL
				if tonumber(decLat) < 0 then
								geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
				else
								geohacklatitude = decLat .. '_N'
				end
				if tonumber(decLong) < 0  then
								geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
				elseif globedata[globe].defaultdisplay == 'dec west' then
								geohacklongitude = decLong .. '_W'
				else
								geohacklongitude = decLong .. '_E'
				end
				-- prepares the 'paramss=' parameter
				local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
				-- concatenate parameteres for geohack
				return i18n.geohackurl ..
								"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
								"&params=" .. geohackparams ..
								(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
end
--HTML builder for a geohack link
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
				-- geohack url
				local url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
		
				-- displayed coordinates
				local displaycoords
				if string.sub(displayformat,1,3) == 'dec' then
								displaycoords = p.displaydec(decLat, decLong, displayformat)
				else
								displaycoords = {
												p.displaydmsdimension(dmsLat, displayformat),
												p.displaydmsdimension(dmsLong, displayformat),
								}
				end
		
				-- build coordinate in h-geo / h-card microformat
				local globeNode
				if globe and globe ~= 'earth' then
								globeNode = mw.html.create('data')
												:addClass('p-globe')
												:attr{ value = globe }
												:done()
				end
		
				local coordNode = mw.html.create('')
				if objectname then
								coordNode = mw.html.create('span')
												:addClass('h-card')
												:tag('data')
																:addClass('p-name')
																:attr{ value = objectname }
																:done()
				end
				coordNode
								:tag('span')
												:addClass('h-geo')
												:addClass('geo-' .. string.sub(displayformat,1,3))
												:tag('data')
																:addClass('p-latitude')
																:attr{ value = decLat }
																:wikitext( displaycoords[1] )
																:done()
												:wikitext(", ")
												:tag('data')
																:addClass('p-longitude')
																:attr{ value = decLong }
																:wikitext( displaycoords[2] )
																:done()
												:node( globeNode )
												:done()
		
				-- buid GeoHack link
				local root = mw.html.create('span')
								:addClass('plainlinks nourlexpansion')
								:attr('title', i18n.tooltip)
								:wikitext('[' .. url )
								:node(coordNode)
								:wikitext("]")
								:done()
		
				-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
				local inlineText = displayinline and tostring(root) or ''
				local titleText = ''
				if displaytitle then
								local htmlTitle = mw.html.create('span')
												:attr{ id = 'coordinates' }
												:addClass( displayinline and 'noprint' or nil )
												:node( root )
								local frame = mw.getCurrentFrame()
								titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' }    )
				end
		
				return inlineText .. titleText
end
local function zoom( extraparams )
				local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )
				if zoomParam then
								return zoomParam
				end
		
				local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
				if scale then
								return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25)
				end
		
				local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
				if extraType then
								local zoomType = {
												country = 5,
												state = 6,
												adm1st = 7,
												adm2nd = 8,
												city = 9,
												isle = 10,
												mountain = 10,
												waterbody = 10,
												airport = 12,
												landmark = 13,
								}
								return zoomType[ extraType ]
				end
end
--HTML builder for a geohack link
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )
				-- displayed coordinates
				local displaycoords
				if string.sub(displayformat,1,3) == 'dec' then
								displaycoords = p.displaydec(decLat, decLong, displayformat)
				else
								displaycoords = {
												p.displaydmsdimension(dmsLat, displayformat),
												p.displaydmsdimension(dmsLong, displayformat),
								}
				end
		
				-- JSON for maplink
				local jsonParams = {
								type = 'Feature',
								geometry = {
												type ='Point',
												coordinates = {
																math_mod._round( decLong, 6 ), -- max precision in GeoJSON format
																math_mod._round( decLat, 6 )
												}
								},
								properties = {
												['marker-color'] = "228b22",
								}
				}
				if objectname then
								jsonParams.properties.title = objectname
				end
				-- adicionar a geoshape via externaldata
				local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )
				if not geoshape and displaytitle and mw.wikibase.getEntity() then
								geoshape = mw.wikibase.getEntity().id
				end
				if geoshape then
								jsonParams = {
												jsonParams,
												{
																type = 'ExternalData',
																service = 'geoshape',
																ids = geoshape,
																properties = {
																				['fill-opacity'] = 0.2
																}
												}
								}
				end
				local maplink = mw.getCurrentFrame():extensionTag{
								name = 'maplink',
								content = mw.text.jsonEncode( jsonParams ),
								args = {
												text = displaycoords[1] .. ", " .. displaycoords[2],
												zoom = zoom( extraparams ) or default_zoom,
												latitude = decLat,
												longitude = decLong,
								}
				}
		
				-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
				local inlineText = displayinline and maplink or ''
				local titleText = ''
				if displaytitle then
								local htmlTitle = mw.html.create('span')
												:attr{ id = 'coordinates' }
												:addClass( displayinline and 'noprint' or nil )
												:wikitext( maplink )
								local frame = mw.getCurrentFrame()
								titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' }    )
				end
		
				return inlineText .. titleText
end
-- dms specific funcions
local function twoDigit( value )
				if ( value < 10 ) then
								value = '0' .. lang:formatNum( value )
				else
								value = lang:formatNum( value )
				end
				return value
end
function p.displaydmsdimension(valuetable, format) -- formato em latitude ou longitude dms
				local str = ''
				local direction = valuetable.direction
				local degrees, minutes, seconds = '', '', ''
				local dimension
				if format == 'dms long' then
								direction = i18n[direction .. 'long']
				else
								direction = i18n[direction]
				end
				degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees
		
				if valuetable.minutes then
								minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
				end
				if valuetable.seconds then
								seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
				end
				return degrees .. minutes .. seconds .. direction
end
local function validdms(coordtable)
				local direction = coordtable.direction
				local degrees = coordtable.degrees or 0
				local minutes = coordtable.minutes or 0
				local seconds = coordtable.seconds or 0
				local dimension = coordtable.dimension
				if not dimension then
								if direction == 'N' or direction == 'S' then
												dimension = 'latitude'
								elseif direction == 'E' or direction == 'W' then
												dimension = 'longitude'
								else
												makeerror({message = i18n.invalidNSEW, sortkey = 'A'})
												return false
								end
				end
				if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
								makeerror({message = i18n.invalidFormat, sortkey = 'A'})
								return false
				end
		
				if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
								makeerror({message = i18n.invalidNS, sortkey = 'A'})
								return false
				end
				if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
								makeerror({message = i18n.invalidEW, sortkey = 'A'})
								return false
				end
		
				if dimension == 'latitude' and degrees > 90 then
								makeerror({message = i18n.latitude90, sortkey = 'A'})
								return false
				end
		
				if dimension == 'longitude' and degrees > 360 then
								makeerror({message = i18n.longitude360, sortkey = 'A'})
								return false
				end
		
				if degrees < 0 or minutes < 0 or seconds < 0 then
								makeerror({message = i18n.negativeCoode, sortkey = 'A'})
								return false
				end
		
				if minutes > 60 or seconds > 60 then
								makeerror({message = i18n.minSec60, sortkey = 'A'})
								return false
				end  
				if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
								makeerror({message = i18n.dmIntergers, sortkey = 'A'})
								return false
				end
				return true
end
local function builddmsdimension(degrees, minutes, seconds, direction, dimension)
				-- no error checking, done in function validdms
				local dimensionobject = {}
		
				-- direction and dimension (= latitude or longitude)
				dimensionobject.direction = direction
				if dimension then
								dimensionobject.dimension = dimension
				elseif direction == 'N' or direction == 'S' then
								dimensionobject.dimension = 'latitude'
				elseif direction == 'E' or direction == 'W' then
								dimensionobject.dimension = 'longitude'
				end
		
				-- degrees, minutes, seconds
				dimensionobject.degrees = tonumber(degrees)
				dimensionobject.minutes = tonumber(minutes)
				dimensionobject.seconds = tonumber(seconds)
				if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' end
				if minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' end
				if seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' end
				return dimensionobject
end
function p._parsedmsstring( str, dimension ) -- pega uma sequência e dá nomes aos parâmetros
				-- output table: { latitude=, longitude = , direction =  }
				if type( str ) ~= 'string' then
								return nil
				end
				str = mw.ustring.gsub( mw.ustring.upper( str ), '%a+', coordParse )
				if not tonumber( str ) and not str:find( '/' ) and str:find( '°' ) then
								local str2 = mw.ustring.gsub( str, '[°″′\"\'\194\160 ]+', '/' )
								-- avoid cases were there is degree ans seconds but no minutes
								if not mw.ustring.find( str, '[″"]' ) or mw.ustring.find( str, '%d[′\'][ \194\160%d]' ) then
												str = str2
								end
				end
				if not tonumber(str) and not string.find(str, '/') then
								makeerror({message = i18n.invalidFormat, sortkey= 'A'})
								return nil
				end
				args = mw.text.split(str, '/', true)
				if #args > 4 then
								makeerror({message = i18n.tooManyParam, sortkey= 'A' })
				end  
				local direction = mw.text.trim(args[#args])
				table.remove(args)
				local degrees, minutes, seconds = args[1], args[2], args[3]
				local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction, dimension)
				if validdms(dimensionobject) then
								return dimensionobject
				else
								return nil
				end
end
--- decimal specific functions
function p.displaydec(latitude, longitude, format)
				lat = lang:formatNum( latitude )
				long = lang:formatNum( longitude )
		
				if format == 'dec west' or  format == 'dec east' then
								local symbolNS, symbolEW = i18n.N, i18n.E
								if latitude < 0 then
												symbolNS = i18n.S
												lat = lang:formatNum( -latitude )
								end
								if format == 'dec west' then
												symbolEW = i18n.W
								end
								if longitude < 0 then
												long = lang:formatNum( 360 + longitude )
								end
						
								return { lat .. i18n.degrees .. symbolNS,  long ..  i18n.degrees .. symbolEW }
						
				else
								return { lat, long }
				end
end
local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
				dec = mw.text.trim(dec)
				if not dec then
								return nil
				end
				if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
								makeerror({'invalid coord type', sortkey = "A"})
								return nil
				end
				local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
				if not numdec then -- tries the decimal + direction format
								dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
								local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
								dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
								if not dec or not tonumber(dec) then
												return nil
								end
								if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
												numdec = tonumber( dec )
								elseif direction == 'W' or direction == 'S' then
												dec = '-' .. dec
												numdec = tonumber( dec )
								else
												if coordtype == 'latitude' then
																makeerror({message = i18n.invalidNS, sortkey = 'A'})
												else
																makeerror({message = i18n.invalidEW, sortkey = 'A'})
												end
												return nil
								end
				end
				if coordtype == 'latitude' and math.abs(numdec) > 90 then
								makeerror({message = i18n.latitude90 , sortkey = 'A'})
								return nil
				end
				if coordtype == 'longitude' and math.abs(numdec) > 360 then
								makeerror({message = i18n.longitude360 , sortkey = 'A'})
								return nil
				end
				return dec
end
-- dms/dec conversion functions
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
				if precision >= 3 then
								return 'dms'
				elseif precision >=1 then
								return 'dm'
				else
								return 'd'
				end
end
local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
				local precision = 0
				for d, val in ipairs(decs) do
								precision = math.max(precision, math_mod._precision(val))
				end
				return convertprecision(precision)
end
local function dec2dms_d(dec)
				local degrees = math_mod._round( dec, 0 )
				return degrees
end
local function dec2dms_dm(dec)
				dec = math_mod._round( dec * 60, 0 )
				local minutes = dec % 60
				dec = math.floor( (dec - minutes) / 60 )
				local degrees = dec % 360
				return degrees, minutes
end
local function dec2dms_dms(dec)
				dec = math_mod._round( dec * 60 * 60, 0 )
				local seconds = dec % 60
				dec = math.floor( (dec - seconds) / 60 )
				local minutes = dec % 60
				dec = math.floor( (dec - minutes) / 60 )
				local degrees = dec % 360
				return degrees, minutes, seconds
end
function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
				local degrees, minutes, seconds
		
				-- verificação do globo
				if not ( globe and globedata[ globe ] ) then
								globe = 'earth'
				end
		
				-- precision
				if not precision or precision == '' then
								precision = determinedmsprec({dec})
				end
				if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
								return makeerror({sortkey = 'C'})
				end
				local dec = tonumber(dec)
		
				-- direction
				local direction
				if coordtype == 'latitude' then
								if dec < 0 then
												direction = 'S'
								else
												direction = 'N'
								end
				elseif coordtype == 'longitude' then
								if dec < 0 or globedata[globe].defaultdisplay == 'dec west' then
												direction = 'W'
								else
												direction = 'E'
								end
				end
		
				-- conversion
				dec = math.abs(dec) -- as coordenadas em dms são sempre positivas
				if precision == 'dms' then
								degrees, minutes, seconds = dec2dms_dms(dec)
				elseif precision == 'dm' then
								degrees, minutes = dec2dms_dm(dec)
				else
								degrees = dec2dms_d(dec)
				end
				return builddmsdimension(degrees, minutes, seconds, direction)
end
function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
				args = frame.args
				local dec = args[1]
				if not tonumber(dec) then
								makeerror({message = i18n.invalidFormat, sortkey = 'A'})
								return showerrors()
				end
				local dirpositive = string.lower(args[2] or '')
				local dirnegative = string.lower(args[3] or '')
				local precision = string.lower(args[4] or '')
				local displayformat, coordtype
		
				if dirpositive == 'n' or dirpositive == 'norte' then
								coordtype = 'latitude'
				else
								coordtype = 'longitude'
				end
				if dirpositive == 'norte' or dirpositive == 'leste' or dirnegative == 'oeste' or dirnegative == 'sul' then
								displayformat = 'dms long'
				end
				local coordobject = p._dec2dms(dec, coordtype, precision)
				if coordobject then
								return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
				else
								return showerrors()
				end
end
function p._dms2dec(dmsobject) -- transformar uma tabela de minutos de segundo em um número decimal
				local direction, degrees, minutes, seconds = dmsobject.direction, dmsobject.degrees, dmsobject.minutes, dmsobject.seconds
				local factor = 0
				local precision = 0
				if not minutes then minutes = 0 end
				if not seconds then seconds = 0 end
		
				if direction == "N" or direction == "E" then
								factor = 1
				elseif direction == "W" or direction == "S" then
								factor = -1
				elseif not direction then
								makeerror({message = i18n.noCardinalDirection, sortkey = 'A'})
								return nil
				else
								makeerror({message = i18n.invalidDirection, sortkey = 'A'})
								return nil
				end
		
				if dmsobject.seconds then -- verifique a precisão dos dados iniciais
								precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passagem por cadeias de texto bastante complicada?
				elseif dmsobject.minutes then
								precision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )
				else
								precision = math.max( math_mod._precision(tostring(degrees), 0 ) )
				end
		
				local decimal = factor * (degrees+(minutes+seconds/60)/60)
				return math_mod._round(decimal, precision)
end
function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax
				local args = frame.args
				if tonumber(args[1]) then
								return args[1] -- coordenadas já em decimal
				elseif not args[2] then
								local dmsobject = p._parsedmsstring(args[1])
								if dmsobject then
												return p._dms2dec(dmsobject) -- coordena sob a proa 23/22/N
								else
												local coordType
												if args[1]:match( '[NS]' ) then
																coordType = 'latitude'
												elseif args[1]:match( '[EWO]') then
																coordType = 'longitude'
												end
												if coordType then
																local result = parsedec( args[1],  coordType, args.globe or 'earth' )
																if result then
																				return result
																end
												end
												return showerrors()
								end
				else
								return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})
				end
end
-- Wikidata
local function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"
				if precision < 0.016 then
								return 'dms'
				elseif precision < 1 then
								return 'dm'
				else
								return 'd'
				end
end
local function wikidatacoords(query)
				query = query or {property = 'p625'}
				query.formatting = 'raw'
				local wd = require('Módulo:Infobox/Wikidata')
				local claim = wd.getClaims(query)
				if claim and claim[1] then -- redundant but more robust in case of a change in the code of Module:Infobox/Wikidata
								local coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values
								-- Wikidata does not handle correctly +West longitudes
								if globedata[ coords.globe ] and globedata[ coords.globe ].defaultdisplay == 'dec west' then
												coords.longitude = math.abs( coords.longitude )
								end
								return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001)
				end
				return nil
end
local function wikidatacat(globe)
				local entitycat = mw.wikibase.getEntity()
		
				local basecat = '!Páginas com mapas'
				local finalcat = {}
				--BADGES
				if entitycat then
								--BADGES
											for i, badgeId in ipairs( entitycat.sitelinks['ptwiki'].badges ) do
												if badgeId == 'Q17437796'  then
																basecat= string.gsub(basecat, "!Páginas com mapas", "!Artigos por qualidade sobre geografia")
												end
												if badgeId == 'Q17437798'  then
																basecat= string.gsub(basecat, "!Páginas com mapas", "!Artigos bons sobre geografia")
												end
								end
				end
								table.insert(finalcat,basecat)
		
				return finalcat
end
	-- main function for displaying coordinates
function p._coord(args)
				-- I declare variable  
				local displayformat = args.format -- string: one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west'
				local displayplace = string.lower(args.display or 'inline') --string: one of 'inline', 'title' or 'inline,title'
				local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack
				local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates
				local wikidata = args.wikidata -- string: set to "true" if needed
				local wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoords
				local dmslatitude, dmslongitude -- table (when created)  
				local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams)
					local trackingstring = '' -- tracking cats except error cats (already in errorstring)
					local rawlat, rawlong = args.latitude, args.longitude
					if rawlat == '' then rawlat = nil end
					if rawlong == '' then rawlong = nil end
					local globe = string.lower( args.globe or extraparams:match('globe:(%a+)') or '' ) -- string: see the globedata table for accepted values
				local latitude, longitude, precision, dmslatitude, dmslongitude -- latitude and longitude in decimal / dmslatitude and dmslongitude: tables withdms coords
				local maplink = true -- use maplink whenever it is possible
		
				-- II extract coordinates from Wikitext
				if (rawlat or rawlong) then
								if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude
												makeerror({message = i18n.coordMissing, sortkey = 'A'})
												return showerrors()
								end
								latitude = parsedec(rawlat, 'latitude', globe)
								if latitude then -- if latitude is decimal
												longitude = parsedec(rawlong, 'longitude', globe) -- so should be longitude
												precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros
												if not latitude or not longitude then
																if errorstring == '' then
																				makeerror({message = i18n.invalidFormat, sortkey = 'A'})
																end
																return showerrors()
												end
												dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
												latitude, longitude = tonumber(latitude), tonumber(longitude)
								else -- if latitude is not decimal try to parse it as a dms string
												dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude')
												if not dmslatitude or not dmslongitude then
																return showerrors()
												end
												latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)
								end
				end
				-- III extract coordinate data from Wikidata and compare them to local data
				local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
				if wikidata == 'true' then
								wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery)
						
								if wikidatalatitude and latitude and longitude then
												local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
												if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) <  maxdistance then
																trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
																				else
																trackingstring = trackingstring .. makecat(i18n.notaswikidata)
												end
								end
								if wikidatalatitude and not latitude then
												latitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
												dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
												trackingstring = trackingstring .. makecat(i18n.throughwikidata)
								end
		
								if latitude and not wikidatalatitude then
												if mw.title.getCurrentTitle().namespace == 0 then
																trackingstring = trackingstring .. makecat(i18n.nowikidata)
												end
								end
				end
				-- exit if stil no latitude or no longitude
				if not latitude and not longitude then
								return nil -- não adicione nada aqui para que a chamada para esta função retorne nil na ausência de dados
				end
				-- IV best guesses for missing parameters
		
				--- globe
				if globe == '' then
								globe = 'earth'
				end
				if not globedata[globe] then
								makeerror({message = i18n.invalidGlobe .. globe})
								globe = 'earth'
				end
				if globe ~= 'earth' then
								extraparams = extraparams .. '_globe:' .. globe -- não há problema se o globo é duplicado
								maplink = false
				end
		
				--- diplayformat
				if not displayformat or displayformat == '' then
								displayformat = globedata[globe].defaultdisplay
				end
		
				-- displayinline/displaytitle
				local displayinline =  string.find(displayplace, 'inline')
				local displaytitle = string.find(displayplace, 'title')
				if not displayinline and not displaytitle then
								displayinline = true
								if displayplace ~= '' then
												makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue
								end
				end
				if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
								--local cattoappend=globedata[globe].trackingcat
								--Recuperação dos badges
								local cats=wikidatacat(globe)
								for i, cat in ipairs( cats ) do
												trackingstring = trackingstring .. makecat(cat)
								end
		
				end
		
-- V geodata
				local geodata = ''
				if latitude and longitude then
								local latstring, longstring = tostring(latitude), tostring(longitude)
								local primary = ''
								local frame = mw.getCurrentFrame()
								local geodataparams = {[1] = latstring, [2] = longstring, [3] = extraparams }
								if displaytitle then
												geodataparams[4] = 'primary'
								end
								if objectname then
												geodataparams.name = objectname
								end
								geodata = frame:callParserFunction('#coordinates', geodataparams )
								if string.find(geodata, 'error') then -- the only error that has not been caught yet is primary key
												geodata = ''
												makeerror({sortkey='D'})
								end
				end
-- VI final output
				local mainstring = ''
				if maplink then
								mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
				else
								mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
				end
		
				return mainstring .. notes .. trackingstring .. geodata .. showerrors()
end
function p.coord(frame) -- parses the strange parameters of Template:Coord before sending them to p.coord
				local args = frame.args
				local numericargs = {}
				for i, j in ipairs(args) do
								args[i] = mw.text.trim(j)
								if type(i) == 'number' and args[i] ~= '' then
												table.insert(numericargs, args[i])
								end
				end
				if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
								args.extraparams = numericargs[#numericargs]
								if #numericargs == 1 and tonumber(numericargs[1]) then
												makeerror({message = i18n.coordMissing, sortkey = 'A'})
												return showerrors()
								end
								table.remove(numericargs)
				end
				for i, j in ipairs(numericargs) do
								if i <= (#numericargs / 2) then
												if not args.latitude then
																args.latitude = j
												else
																args.latitude =    args.latitude .. '/' .. j
												end
								else
												if not args.longitude then
																args.longitude = j
												else
																args.longitude = args.longitude .. '/' .. j
												end
								end
				end
				if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
								args.latitude, args.longitude = args.longitude, args.latitude
				end
				return p._coord(args)
end
function p.Coord(frame)
				return p.coord(frame)
end
function p.latitude(frame) -- helper function para infobox, a depreciar
				local args = frame.args
				local latitude  = frame.args[1]
				if latitude and mw.text.trim(latitude) ~= '' then
								return latitude
				elseif frame.args['wikidata'] == 'true' then
								local lat, long = wikidatacoords()
								return lat
				end
end
function p.longitude(frame) -- helper function para infobox, a depreciar
				local args = frame.args
				local longitude = frame.args[1]
				if longitude and mw.text.trim(longitude) ~= '' then
								return longitude
				elseif frame.args['wikidata'] == 'true' then
								local lat, long = wikidatacoords()
								return long
				end
end
--[[
coord2text
(documentação a traduzir)
]]
	function p.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
		local result, negative = mw.text.split((mw.ustring.match(frame.args[1],'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')
		if frame.args[2] == 'lat' then
			result, negative = result[1], 'S'
		else
			result, negative = result[2], 'W'
		end
		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]..':(.-)[ _]')
	end
end
--[[
link
Simple function to export the coordinates link for other uses.
Usage:
				{{#invoke:Coordenadas | link }}
	
]]
function p.link(frame)
				return coord_link;
end
return p