モジュール:Coordinates
このモジュールについての説明文ページを モジュール:Coordinates/doc に作成できます
--[[
This module is intended to (eventually) replace some or all functionality of {{location}} and related
templates. At the moment it is collection of methods related to geolocation.
*function coordinates.LocationTemplateCore(frame)
**function coordinates.GeoHack_link(frame)
***function coordinates.lat_lon(frame)
****function coordinates._deg2dms(deg,lang)
***function coordinates.externalLink(frame)
****function coordinates._externalLink(site, globe, latStr, lonStr, lang, attributes)
**function coordinates._getHeading(attributes)
**function coordinates.externalLinksSection(frame)
***function coordinates._externalLink(site, globe, latStr, lonStr, lang, attributes)
*function coordinates.getHeading(frame)
*function coordinates.deg2dms(frame)
]]
coordinates = {};
-- =======================================
-- === Dependencies ======================
-- =======================================
local i18n = require('Module:I18n/coordinates') -- get localized translations of site names
local Fallback = require('Module:Fallback') -- get fallback functions
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,
}
-- URL definitions for different sites. Strings: $lat, $lon, $lang, $attr will be replaced with latitude, longitude, language code and GeoHack attribution parameters strings.
local SiteURL = {
GeoHack = 'http://tools.wmflabs.org/geohack/geohack.php?pagename={{FULLPAGENAMEE}}¶ms=$lat_N_$lon_E_$attr&language=$lang',
GoogleEarth = '{{fullurl:tools:~para/GeoCommons/earth.php|latdegdec=$lat&londegdec=$lon&scale=10000&commons=1}}',
Proximityrama = '{{fullurl:tools:~para/GeoCommons/proximityrama|latlon=$lat,$lon}}',
OpenStreetMap = '{{fullurl:tools:~kolossos/openlayers/commons-on-osm.php|zoom=16&lat=$lat&lon=$lon}}',
GoogleMaps = {
Mars = 'http://www.google.com/mars/#lat=$lat&lon=$lon&zoom=8',
Moon = 'http://www.google.com/moon/#lat=$lat&lon=$lon&zoom=8',
Earth = 'http://maps.google.com/maps?ll=$lat,$lon&spn=0.01,0.01&t=k&q=http://toolserver.org/~para/GeoCommons/GeoCommons-simple.kml&hl=$lang'
}
}
-- Categories
local CoorCat = {
File = '[[Category:Media with locations]]',
Gallery = '[[Category:Galleries with coordinates]]',
Category = '[[Category:Categories with coordinates]]',
globe = '[[Category:Media with %s locations]]',
default = '[[Category:Media with default locations]]',
erroneous = '[[Category:Media with erroneous locations]]<span style="color:red;font-weight:bold">Error: Invalid parameters!</span>\n'
}
-- =======================================
-- === Functions =========================
-- =======================================
-- parse attribute variable returning desired field
function coordinates.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 coordinates.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 = coordinates._getHeading(attributes)
if hNum == nil then
return ''
end
return tostring(hNum)
end
-- Helper core function for getHeading.
function coordinates._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
function coordinates.deg2dms(frame)
local deg = tonumber(frame.args[1])
local lang
if frame.args.lang and mw.language.isSupportedLanguage(frame.args.lang) then
lang = frame.args.lang
else -- get user's chosen language
lang = frame:preprocess( "{{int:lang}}" )
end
if deg==nil then
return frame.args[1];
else
return coordinates._deg2dms(deg,lang)
end
end
-- Helper core function for deg2dms.
function coordinates._deg2dms(deg,lang)
local dNum, mNum, sNum, dStr, mStr, sStr
local Lang = mw.language.new(lang)
deg = math.floor(360000*(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(deg/360000) % 360 -- degree number (integer in 0-360 range)
mNum = math.floor(deg/6000 ) % 60 -- minute number (integer in 0-60 range)
sNum = (deg%6000 ) / 100 -- seconds number (float with 2 decimal digits in 0-60 range)
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
str = string.format('%s° %s′ %s″', dStr, mStr, sStr);
return str
end
-- format coordinate location string
function coordinates.lat_lon(frame)
local lat = tonumber(frame.args.lat)
local lon = tonumber(frame.args.lon)
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
if frame.args.lang and mw.language.isSupportedLanguage(frame.args.lang) then
lang = frame.args.lang
else -- get user's chosen language
lang = frame:preprocess( "{{int:lang}}" )
end
if lat==nil or lon==nil then
return 'latitude, longitude'
else
local nsew = Fallback._langSwitch(i18n.NSEW, lang) -- find set of localized translation of N, S, W and E in the desired language
local SN, EW, latStr, lonStr
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
latStr = coordinates._deg2dms(math.abs(lat), lang) -- Convert latitude degrees to degrees/minutes/seconds
lonStr = coordinates._deg2dms(math.abs(lon), lang) -- Convert longitude degrees to degrees/minutes/seconds
return string.format('%s %s, %s %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 based on globe (planet), latitude, longitude, language code and GeoHack attribution parameters
function coordinates.externalLink(frame)
args = frame.args
if args.lang and mw.language.isSupportedLanguage(args.lang) then
lang = args.lang
else -- get user's chosen language
lang = frame:preprocess( "{{int:lang}}" )
end
return coordinates._externalLink(args.site or 'GeoHack', args.globe or 'Earth', args.lat, args.lon, lang, args.attributes or '')
end
-- Helper core function for externalLink
function coordinates._externalLink(site, globe, latStr, lonStr, lang, attributes)
local str
if site == 'GoogleMaps' then
str = SiteURL.GoogleMaps[globe]
else
str = SiteURL[site];
attributes = string.format('globe:%s_%s', globe, attributes)
str = mw.ustring.gsub( str, '$attr', attributes)
end
str = mw.ustring.gsub( str, '$lat', latStr)
str = mw.ustring.gsub( str, '$lon', lonStr)
str = mw.ustring.gsub( str, '$lang', lang)
return str
end
-- adjust attributes depending on the template that calls it
function coordinates.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
function coordinates.GeoHack_link(frame)
-- create link and coordintate string
local latlon = coordinates.lat_lon(frame)
if latlon=='lattiude, longitude' then
return latlon
else
frame.args.site = 'GeoHack'
local url = frame:preprocess(coordinates.externalLink(frame)) -- use preprocess to get page name
return string.format('<span class="plainlinksneverexpand">[%s %s]</span>', url, latlon) --<span class="plainlinks nourlexpansion">
end
end
function coordinates.externalLinksSection(frame)
args = frame.args
if args.lang and mw.language.isSupportedLanguage(args.lang) then
lang = args.lang
else -- get user's chosen language
lang = frame:preprocess( "{{int:lang}}" )
end
if not args.namespaceNum then
args.namespace = frame:preprocess( "{{NAMESPACE}}" )
end
local str
if args.globe=='Earth' then -- Earth locations will have 3 or 4 links
str = string.format('[%s %s] - [%s %s] - [%s %s]',
coordinates._externalLink('OpenStreetMap', 'Earth', args.lat, args.lon, lang, ''),
Fallback._langSwitch(i18n.OpenStreetMaps, lang),
coordinates._externalLink('GoogleMaps' , 'Earth', args.lat, args.lon, lang, ''),
Fallback._langSwitch(i18n.GoogleMaps, lang),
coordinates._externalLink('GoogleEarth' , 'Earth', args.lat, args.lon, lang, ''),
Fallback._langSwitch(i18n.GoogleEarth, lang))
if args.namespace=="Category" then
str = string.format('%s - [%s %s]', str,
coordinates._externalLink('Proximityrama', 'Earth', args.lat, args.lon, lang, ''),
Fallback._langSwitch(i18n.Proximityrama, lang))
end
elseif args.globe=='Mars' or args.globe=='Moon' then
str = string.format('[%s %s]',
coordinates._externalLink('GoogleMaps', args.globe, args.lat, args.lon, lang, ''),
Fallback._langSwitch(i18n.GoogleMaps, lang))
end
--return frame:preprocess(str) -- use preprocess to expand {{#fullurl}}
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 name: File, Category, (Gallery)
]]
function coordinates.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:preprocess( "{{int:lang}}" ) -- get user's chosen language
end
if not (args.namespace) then -- if not provided than look up
args.namespace = frame:preprocess( "{{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 = coordinates.alterAttributes(args.attributes or '', args.mode)
frame.args = args
-- check for errors and add Geo (microformat) code for machine readability.
local lat = tonumber(args.lat)
local lon = tonumber(args.lon)
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 Categories, geoMicroFormat, coorTag = '', '', ''
-- 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 = CoorCat.default
end
if args.noError==0 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)
-- https://www.mediawiki.org/wiki/Extension:GeoData
if args.namespace == 'File' and Status ~= 'secondary' then -- TODO enable for secondary cases without throwing errors
--coorTag = string.format('{{#coordinates:%f|%f|%s|%s}}', frame.args.lat, frame.args.lon, args.attributes, Status)
coorTag = string.format('{{#coordinates:%10.6f|%11.6f|%s}}', lat, lon, args.attributes)
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 str1, str2, str3, str4, inner_table, heading
str1 = coordinates.GeoHack_link(frame) -- the coordinates and link to GeoHack
heading = coordinates._getHeading(frame.args.attributes) -- get heading arrow section
if heading then
--str1 = string.format('%s <span style="{{Transform-rotate|%f}}">[[File:North Pointer.svg|20px|link=|alt=]]</span>', str1, 360-heading)
local fname = string.format('{{Compass rose file|%f|style=heading}}', heading)
str1 = string.format('%s [[%s|25px|link=|alt=]]', str1, fname, heading)
end
str2 = Fallback._langSwitch(i18n.LocationTemplateLinkLabel, args.lang) -- header of the link section
str3 = coordinates.externalLinksSection(frame) or '' -- external link section
str4 = '[[File:Circle-information.svg|18x18px|alt=info|link=Commons:Geocoding]]'
inner_table = string.format('<td style="border:none;">%s</td><td style="border:none;">%s %s</td><td style="border:none;">%s%s</td>', str1, str2, str3, str4, 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 = Fallback._langSwitch(i18n.CameraLocation, args.lang)
elseif args.mode=='object' then
field_name = Fallback._langSwitch(i18n.ObjectLocation, args.lang)
elseif args.mode=='globe' then
field_list = Fallback._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
local style = frame:preprocess(string.format('{{Infobar-Layout|lang=%s}}',lang))
templateText = string.format('<table %s><tr><th class="type fileinfo-paramfield">%s</th>%s</tr></table>', style, field_name, inner_table)
end
return frame:preprocess(templateText .. Categories .. coorTag)
end
return coordinates