Module:Coordinates
提供: VANGUARD FLIGHT wiki
This page is under construction, or in the middle of an expansion or major redevelopment. |
This module is intended to (eventually) replace the functionality of {{location}} and related templates, but at the moment it is collection of methods related to geolocation. It provides several methods most of them not used yet:
- {{#Invoke:Coordinates | parseAttribute| attribute string | attribute name }} : parse {{location}} attribute parameter attribute string and return value of the attribute name parameter
- {{#Invoke:Coordinates | getHeader| attribute string }} : parse {{location}} attribute parameter attribute string and return the numeric value of the header attribute. Careful, this function is live (called by {{Location/layout}}).
- {{#Invoke:Coordinates | GeoHack_link| lat=... | lon=... |lang=xx | site=... | globe=... }}: creates link to GeoHack tool and display location coordinates. The URLs are based on website and latitude/longitude coordinates. Language is used so it can be passes to the website. Globe parameter is used to allow specifying coordinates on planets other than earth. Careful, this function is live (called by {{Institution/coordinates}}).
- {{#Invoke:Coordinates | lat_lon| lat=... | lon=... |lang=xx }}: create coordinate location string based on decimal degrees latitude and longitude number. Language is used to localize the presentation of the numbers.
- {{#Invoke:Coordinates | deg2dms| degrees|lang=xx }}: create dms (degree/minute/second) string based on decimal degrees number. Language is used to localize the presentation of the numbers.
- {{#Invoke:Coordinates | externalLink| site=... | globe=... | lat=... | lon=... |lang=xx }}: create URLs for different sites which are used by coordinate location templates. The URLs are based on website and latitude/longitude coordinates. Language is used so it can be passes to the website. Globe parameter is used to allow specifying coordinates on planets other than earth.
Examples
- {{#invoke:Coordinates| GeoHack_link | lat=51.48 | lon=0}} will display
- {{#invoke:Coordinates| GeoHack_link | lat=51.48 | lon=0 | lang=en }} will display
- {{#invoke:Coordinates| GeoHack_link | lat=51.48 | lon=0 | lang=ru }} will display
- {{#invoke:Coordinates| GeoHack_link | lat= | lon=0 | lang=ru }} (with missing latitude value) will display " "
- {{#invoke:Coordinates| externalLinksSection | globe = Earth| lat=51.48 | lon=0 | lang=en | namespace=File}} displays "[{{fullurl:tools:~kolossos/openlayers/commons-on-osm.php|zoom=16&lat=51.48&lon=0}} OpenStreetMap] - Google Maps - [{{fullurl:tools:~para/GeoCommons/earth.php|latdegdec=51.48&londegdec=0&scale=10000&commons=1}} Google Earth]"
- {{#invoke:Coordinates| externalLinksSection | globe = Earth| lat=51.48 | lon=0 | lang=en | namespace=Category}} displays "[{{fullurl:tools:~kolossos/openlayers/commons-on-osm.php|zoom=16&lat=51.48&lon=0}} OpenStreetMap] - Google Maps - [{{fullurl:tools:~para/GeoCommons/earth.php|latdegdec=51.48&londegdec=0&scale=10000&commons=1}} Google Earth]"
- {{#invoke:Coordinates| externalLinksSection | globe = Earth| lat=51.48 | lon=0 | lang=ru | namespace=Category}} displays "[{{fullurl:tools:~kolossos/openlayers/commons-on-osm.php|zoom=16&lat=51.48&lon=0}} OpenStreetMap] - Картах Google - [{{fullurl:tools:~para/GeoCommons/earth.php|latdegdec=51.48&londegdec=0&scale=10000&commons=1}} Google Планете Земля]"
- {{#invoke:Coordinates| externalLinksSection | globe = Mars| lat=51.48 | lon=0 | lang=en | namespace=File}} displays "Google Maps"
- {{#invoke:Coordinates| externalLinksSection | globe = Moon| lat=51.48 | lon=0 | lang=en | namespace=File}} displays "Google Maps"
- {{#invoke:Coordinates| LocationTemplateCore | globe = Earth| lat=51.48 | lon=0 | lang=en | namespace=File| attributes=elevation:10_heading:W | mode=camera | bare = 1}} displays "
{{#coordinates: 51.480000| 0.000000|type:camera_elevation:10_heading:W}}"View this and other nearby images on: OpenStreetMap - Google Maps - Google Earth - {{#invoke:Coordinates| LocationTemplateCore | globe = Earth| lat=51.48 | lon=0 | lang=en | namespace=File| attributes=elevation:10_heading:W | mode=camera | bare = 0}} displays "
{{#coordinates: 51.480000| 0.000000|type:camera_elevation:10_heading:W}}"Camera location View this and other nearby images on: OpenStreetMap - Google Maps - Google Earth
See testcases to see more examples.
Dependencies
Relies on Module:I18n/coordinates for all of the internationalization translations.
--[[
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