உள்ளடக்கத்துக்குச் செல்

Module:Coordinates

விக்கிமூலம் இலிருந்து

Documentation for this module may be created at Module:Coordinates/doc

--[[

This module is intended to provide functionality of {{location}} and related
templates. 

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)

]]

-- =======================================
-- === Dependencies ======================
-- =======================================
local i18n  = require('Module:I18n/coordinates')    -- get localized translations of site names
local yesno = require('Module:Yesno')

-- =======================================
-- === Hardwired parameters ==============
-- =======================================

-- Angles associated with each abriviation 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,
}

-- 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'
}

-- 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&params=$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',
	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'
	}
}

-- Categories
local CoorCat = {
	File          = '[[Category:Media with locations]]',
	Gallery       = '[[Category:Galleries with coordinates]]',
	Category      = '[[Category:Categories with coordinates]]',
	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 NoLatLonString = 'latitude, longitude'

local function langSwitch(list,lang)
	local langList = mw.language.getFallbacksFor(lang)
	table.insert(langList,1,lang)
	for i,language in ipairs(langList) do
		if list[language] then
			return list[language]
		end
	end
end

local function add_maplink(lat, lon, marker, text)
	local tstr = ''
	if text then
		tstr = string.format('text="%s" ', text)
	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

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; ">'..
		'<tr>'..
		'<td class="mbox-image" style="padding-left:.9em;">'..
		' [[File:Commons-emblem-issue.svg|class=noviewer|45px]]</td>'..
		'<td class="mbox-text" style="">%s</td>'..
		'</tr></table>', text)
end

local function mergeWithWikidata(q, lat1, lon1)
	-- we are given wikidata q-code so look up the coordinates
	local dist_str=''
	-- Wikiata coordinates
	if q:match( '^[Qq]%d+$' ) then
		entity = mw.wikibase.getEntity(q)
	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
			
	-- 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/#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/#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
		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
	
	-- 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
		Q12308941 = 1, -- male given name 
		Q4167836  = 1, -- Wikimedia category
		Q4167410  = 1, -- Wikimedia disambiguation page
		Q783794   = 2, -- company
		Q4830453  = 2, -- business enterprise
	}
	s = entity:getBestStatements( 'P31' )
	if s[1] and s[1].mainsnak.datavalue.value['numeric-id'] then
		local instanceOf = string.format('Q%i',s[1].mainsnak.datavalue.value['numeric-id'])
		if QCodes[instanceOf] then
			cat = '' -- wipe out categories
			if QCodes[instanceOf]==1 then -- add problem category
				cat = CoorCat.wikidata5 
			end
		end
	end
	
	return lat1, lon1, q, cat, dist_str
end

-- =======================================
-- === Functions =========================
-- =======================================
p = {}

-- parse attribute variable returning desired field
function p.parseAttribute(frame)
  return string.match(mw.text.decode(frame.args[1]), mw.text.decode(frame.args[2]) .. ':' .. '([^_]*)') or ''
end

--[[============================================================================
Parse attribute variable returning heading field. If heading is a string than 
try to convert it to an angle
==============================================================================]]
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
	local hNum = p._getHeading(attributes)
	if hNum == nil then
		return ''
	end
	return tostring(hNum)
end
-- Helper core function for getHeading. 
function p._getHeading(attributes)
	if attributes == nil then
		return nil
	end
	local hStr = string.match(mw.text.decode(attributes), 'heading:([^_]*)')
	if hStr == nil then
		return nil
	end
	local hNum = tonumber( hStr )
	if hNum == nil then
		hStr = string.upper (hStr)
		hNum = compass_points[hStr]  
	end
	if hNum ~= nil then
		hNum = hNum%360
	end
	return hNum
end

--[[============================================================================
Convert degrees to degrees/minutes/seconds notation comonly used when displaying 
coordinates.
Inputs:
1) latitude or longitude angle in degrees
2) georeference precission in degrees
3) language used in formating of the number
==============================================================================]]
function p.deg2dms(frame)
	local deg     = tonumber(frame.args[1])
	local degPrec = tonumber(frame.args[2]) or 0-- precision in degrees
	local lang    =  frame.args.lang
	if not (lang and mw.language.isSupportedLanguage(lang)) then 
		lang = frame:callParserFunction("int","lang")  -- get user's chosen language
	end
	if deg==nil then
		return frame.args[1];
	else
		return p._deg2dms(deg, degPrec, lang)
	end
end

--[[============================================================================
Helper core function for deg2dms. deg2dms can be called by templates, while 
_deg2dms should be called from Lua.
Inputs:
* deg - positive coordinate in degrees
* degPrec - coordinate precission in degrees will result in different angle format
* lang - language to used when formating the number
==============================================================================]]
function p._deg2dms(deg, degPrec, lang)
	local dNum, mNum, sNum, dStr, mStr, sStr, formatStr, secPrec, c, k
	local Lang = mw.language.new(lang)

	-- adjust number display based on precission
	secPrec = degPrec*3600.0                     -- coordinate precission in seconds
	if secPrec<0.05 then                         -- degPrec<1.3889e-05
		formatStr = '%s°&nbsp;%s′&nbsp;%s″'        -- use DD° MM′ SS.SS″ format
		c = 360000
	elseif secPrec<0.5 then                      -- 1.3889e-05<degPrec<1.3889e-04
		formatStr = '%s°&nbsp;%s′&nbsp;%s″'        -- use DD° MM′ SS.S″ format
		c = 36000
	elseif degPrec*60.0<0.5 then                 -- 1.3889e-04<degPrec<0.0083
		formatStr = '%s°&nbsp;%s′&nbsp;%s″'        -- use DD° MM′ SS″ format
		c = 3600
	elseif degPrec<0.5 then                      -- 0.0083<degPrec<0.5
		formatStr = '%s°&nbsp;%s′'                 -- use DD° MM′ format
		c = 60
	else -- if degPrec>0.5 then                  
		formatStr = '%s°'                          -- use DD° format
		c = 1
	end
	
	-- create degree, minute and seconds numbers and string
	d = c/60
	k  = math.floor(c*(deg%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 
	if mNum<10 then
		mStr = '0' ..mStr                 -- pad with zero if a single digit
	end
	if sNum<10 then
		sStr = '0' ..sStr                 -- pad with zero if less than ten
	end
	return string.format(formatStr, dStr, mStr, sStr);
end

--[[============================================================================
Format coordinate location string, by creating and joining DMS strings for 
latutude and longitude. Also convert precission from meters to degrees.
INPUTS:
 * lat        = latitude in degrees
 * lon        = longitude in degrees
 * lang       = language code
 * prec       = geolocation precission in meters
==============================================================================]]
function p.lat_lon(frame)
	local lat  = tonumber(frame.args.lat)
	local lon  = tonumber(frame.args.lon)
	local prec = math.abs(tonumber(frame.args.prec) or 0) -- location precision in meters
	if lon then -- get longitude t0 be in -180 to 180 range
		lon=lon%360
		if lon>180 then
			lon = lon-360
		end
	end
	local lang =  frame.args.lang
	if not (lang and mw.language.isSupportedLanguage(lang)) then 
		lang = frame:callParserFunction("int","lang")  -- get user's chosen language
	end
	if lat==nil or lon==nil then
		return NoLatLonString
	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 precission 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&nbsp;%s, %s&nbsp;%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

--[[============================================================================
Create URL for different sites.
INPUTS:
 * site       = Possinle 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.
 * lat        = latitude string or number
 * lon        = longitude string or number
 * lang       = language code
 * attributes = attributes to be passed to GeoHack
==============================================================================]]
function p.externalLink(frame)
	args = frame.args
	local lang =  frame.args.lang
	if not (lang and mw.language.isSupportedLanguage(lang)) then 
		lang = frame:callParserFunction("int","lang")  -- get user's chosen language
	end
	return p._externalLink(args.site or 'GeoHack', args.globe or 'Earth', args.lat, args.lon, lang, args.attributes or '')
end
 
--[[============================================================================
Helper core function for externalLink. Create URL for different sites:
INPUTS:
 * site       = Possinle 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
	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

--[[============================================================================
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
		end
	elseif mode=='object'or mode =='globe' then              -- Used by {{Object location}}
		if string.find(attributes, 'class:object')==nil then
			attributes = 'class:object_' .. attributes
		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}}
		attributes = 'type:user_location'
	elseif mode=='institution' then                          --Used by {{Institution/coordinates}} (categories only)	
		attributes = 'type:institution'
	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 precission in meters
 * attributes = attributes to be passed to GeoHack
==============================================================================]]
function p.GeoHack_link(frame)
	-- create link and coordintate string
	local latlon = p.lat_lon(frame)
	if latlon==NoLatLonString then
		return latlon
	else
		frame.args.site = 'GeoHack'
		local url = p.externalLink(frame)
		return string.format('<span class="plainlinksneverexpand">[%s %s]</span>', url, latlon) --<span class="plainlinks nourlexpansion">
	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(frame)
	local args = frame.args
	local lang = frame.args.lang
	if not (lang and mw.language.isSupportedLanguage(lang)) then 
		lang = frame:callParserFunction("int","lang")  -- get user's chosen language
	end
	if not args.namespace then
		args.namespace = mw.title.getCurrentTitle().namespace
	end
	
	local str, link1, link2
	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] - [%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
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 metchod/template:
 * 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 precission in meters
==============================================================================]]
function p.LocationTemplateCore(frame)
	-- prepare arguments
	args = frame.args
	if not args or not args.lat then -- if no arguments provided than use parent arguments
		args = mw.getCurrentFrame():getParent().args
	end
	if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then 
		args.lang = frame:callParserFunction("int","lang") -- get user's chosen language 
	end
	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
	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)
	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 wikidata link provided than compare coordinates
	local Categories, geoMicroFormat, coorTag, wikidata_link = '', '', '', ''
	if args.wikidata and args.wikidata~='' then
		local dist_str
	  -- 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)
	frame.args = args

	-- 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...
			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 frame.args.attributes and string.find(frame.args.attributes, '=') 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)
			-- add {{#coordinates}} tag, see https://www.mediawiki.org/wiki/Extension:GeoData
			if args.namespace == 'File' and Status == 'primary' and args.mode=='camera' then 
				coorTag = string.format('{{#coordinates:primary|%10.6f|%11.6f|%s}}', lat, lon, args.attributes)
				coorTag = frame:preprocess(coorTag)
			end
		else -- if lat and lon are not numbers then add error category
			Categories = Categories .. CoorCat.erroneous
		end

	end

	-- Call helper functions to render different parts of the template
	local coor,  info_link, inner_table, heading, OSM = '','','','','','',''
	coor = p.GeoHack_link(frame)  									-- the p and link to GeoHack
	heading = p._getHeading(frame.args.attributes)	-- 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&nbsp;&nbsp;<span title="%s°">[[%s|25px|link=|alt=Heading=%s°]]</span>', coor, heading, fname, heading)
	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
	external_link = p.externalLinksSection(frame) 					-- external link section
	if external_link and args.namespace == 'File' then
		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
	info_link   = string.format('[[File:Circle-information.svg|18x18px|alt=info|link=%s]]', langSwitch(i18n.COM_GEO, args.lang) )
	inner_table = string.format('<td style="border:none;">%s&nbsp;%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)
	
	-- combine strings into a table
	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'
		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
			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
		--Create HTML text
		local dir, text_align
		if mw.language.new( args.lang ):isRTL() then
			dir = 'rtl'
			text_align = 'right'
		else
			dir = 'ltr'
			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
	return templateText .. Categories .. coorTag
end

return p
"https://ta.wikisource.org/w/index.php?title=Module:Coordinates&oldid=582936" இலிருந்து மீள்விக்கப்பட்டது