Module:Sandbox/trappist the monk/nts
require ('strict'); -- alarm when global variables etc are used
local nts = require ('Module:Canada NTS'); -- for extents_from_grid(), nts_series_validate, nts_are_validate, nts_sheet_validate
local data = mw.loadData ('Module:Canada NTS/data'); -- load the ~/data module
--[[--------------------------< W I K I D A T A _ L A T _ L O N G _ G E T >------------------------------------
returns latitude and longitude from wikidata for
]]
local function wikidata_lat_lon_get (qid)
if qid then
local value_t = mw.wikibase.getBestStatements (qid, 'P625')[1]; -- attempt to get P625; nil when article does not have P625
if value_t then
value_t = value_t.mainsnak.datavalue.value; -- point to the value table
return value_t.latitude, -1.0 * value_t.longitude; -- return coordinates from value_t; longitude normalized
end
end
end
--[[--------------------------< T A B B I N G >----------------------------------------------------------------
return the number of tabs necessary to more-or-less position comments at column 80 (the right-side margin line
in the wiki code editor)
]]
local function tabbing (str)
return math.ceil ((80 - 4 - str:len()) / 4); -- the -4 because every line begins with a tab character
end
--[[--------------------------< S O R T >----------------------------------------------------------------------
sort by keys; ascending order numerically by series then by area (alpha sort) then numerically by sheet
]]
local function sort (a, b)
local a_key_series, a_key_area, a_key_sheet = a:match ('(%d+)(%u)(%d*)'); -- extract series, area, sheet NTS id parts from
local b_key_series, b_key_area, b_key_sheet = b:match ('(%d+)(%u)(%d*)'); -- extract series, area, sheet NTS id parts from
a_key_series = tonumber (a_key_series); -- convert numerical parts of key from to numbers for comparison
a_key_sheet = tonumber (a_key_sheet); -- nil when
a_key_sheet = a_key_sheet and a_key_sheet or 0 -- optional so when omitted set to 0 for comparison
b_key_series = tonumber (b_key_series); -- convert numerical parts of key from to numbers for comparison
b_key_sheet = tonumber (b_key_sheet); -- nil when
b_key_sheet = b_key_sheet and b_key_sheet or 0 -- optional so when omitted set to 0 for comparison
if a_key_series ~= b_key_series then -- do the comparisons; by series first
return a_key_series < b_key_series;
elseif a_key_area ~= b_key_area then -- then by area
return a_key_area < b_key_area;
else
return a_key_sheet < b_key_sheet; -- and lastly by sheet
end
end
--[[--------------------------< L A T _ B O U N D S _ C H E C K >----------------------------------------------
is
returns direction 'north' or 'south' when
]]
local function lat_bounds_check (lat, north, south)
if lat < south then
return 'south'; -- out of bounds south of
elseif lat > north then
return 'north'; -- out of bounds north of
end
end
--[[--------------------------< L O N _ B O U N D S _ C H E C K >----------------------------------------------
is
returns direction 'east' or 'west' when
]]
local function lon_bounds_check (lon, west, east)
if lon < east then
return 'east'; -- out of bounds east of
elseif lon > west then
return 'west'; -- out of bounds west of
end
end
--[[--------------------------< I S _ I N _ B O U N D S >------------------------------------------------------
get map extents using series, area, sheet. check to see that lat/lon fall within the map extents.
returns empty string for concatenation when lat/lon fall within map extents; an error message else
only one error mesage is returned; if both
message for only one of them
because series, area, sheet are taken from Module:Canada NTS/data, they are presumed to be correct
]]
local function is_in_bounds (series, area, sheet, lat, lon)
local extents_t = nts.extents_from_grid (tonumber(series), area, tonumber(sheet)); -- fetch extents for this nts key
local lat_fail, lon_fail; -- flags
local north, west, south, east; -- map extents
if '' == sheet then -- is this searies, area, sheet an area map?
north, west, south, east = extents_t.area_north, extents_t.area_west, extents_t.area_south, extents_t.area_east;
lat_fail = lat_bounds_check (lat, north, south);
lon_fail = lon_bounds_check (lon, west, east);
else -- here when sheet map
north, west, south, east = extents_t.north, extents_t.west, extents_t.south, extents_t.east;
lat_fail = lat_bounds_check (lat, north, south);
lon_fail = lon_bounds_check (lon, west, east);
end
if lat_fail or lon_fail then -- build error message
if 'north' == lat_fail then
return string.format ('P625 %s latitude out of bounds: WD lat/lon: <%s>,%s (NW: <%s>,%s / SE: %s,%s)',
lat_fail, lat, lon, north, west, south, east);
elseif 'south' == lat_fail then
return string.format ('P625 %s latitude out of bounds: WD lat/lon: <%s>,%s (NW: %s,%s / SE: <%s>,%s)',
lat_fail, lat, lon, north, west, south, east);
elseif 'west' == lon_fail then
return string.format ('P625 %s longitude out of bounds: WD lat/lon: %s,<%s> (NW: %s,<%s> / SE: %s,%s)',
lon_fail, lat, lon, north, west, south, east);
elseif 'east' == lon_fail then
return string.format ('P625 %s longitude out of bounds: WD lat/lon: %s,<%s> (NW: %s,%s / SE: %s,<%s>)',
lon_fail, lat, lon, north, west, south, east);
end
end
return ''; -- in bounds so return empty string message for concatenation
end
--[[--------------------------< M A I N >----------------------------------------------------------------------
fetch
en.wiki article title from wikidata. Replace article title with the qid, sort and make all pretty like for
manual replacement in ~/data.
to use the results of this function, Module:Canada NTS requires support for qids in lieu of article names which,
as of 2022-04-12, does not yet exist
This function may be called from an invoke or from the debug console
is a text string when called from debug console:
=p.main ('92')
{{#invoke:Sandbox/trappist the monk/nts|main|94}}
]]
local function main (frame)
local map_series; -- number of the series that we are operating on; for invokes, this is only number or start of a range
local map_series_end; -- for invokes only; end of a range of map series
local invoked;
if 'table' == type (frame) then
map_series = frame.args[1]; -- for an invoke
if frame.args[2] then
map_series_end = frame.args[2];
end
invoked = true; -- flag used to modify final output rendering
else
map_series = frame; -- when called from the debug console
end
local temp_t = {} -- output goes in this sequence table until rendering
for k, v in pairs (data) do -- for each entry in the data mofule
local series, area, sheet = k:match ('^(%d+)(%u)(%d*)'); -- fetch series, area, sheet from the entry's key
if tonumber(series) >= tonumber(map_series) and tonumber(series) <= tonumber(map_series_end or map_series) then -- when this map is one of the map series we're looking for
local map_title_parts = mw.text.split (v, '|'); -- extract an article title
local qid = mw.wikibase.getEntityIdForTitle (map_title_parts[1]); -- does this article title have a wikidata entry
local lat, lon = wikidata_lat_lon_get (qid); -- attempt to get P625 lat/lon from wikidata's entry for the article title
local bounds_msg = ''; -- gets an error message if lat/lon from wikidata not within bounds of NTS map
if qid and lat then -- when article title has wikidata entry an wikidata has P625 (latitude/longitude)
bounds_msg = is_in_bounds (series, area, sheet, lat, lon); -- empty string when in bounds; message else
elseif qid then -- lat can be nil when qid points to dab page or article does not have P625
bounds_msg = string.format ('%s missing P625', qid); -- no lat/lon so no P625
end
local str;
if not qid then -- when article title doesn't have a wikidata entry
str = string.format ('\t["%s"] = "%s",', k, v); -- mimic the original
elseif not lat then -- has qid but does not have P625
str = string.format ('["%s"] = "%s",', k, v); -- mimic the original + bounds_msg
str = string.format ('\t%s%s-- %s', str, string.rep ('\t', tabbing (str)), bounds_msg);
elseif map_title_parts[2] then -- for piped article links
str = string.format ('["%s"] = "%s|%s",', k, qid, map_title_parts[2]);
str = string.format ('\t%s%s-- %s; %s', str, string.rep ('\t', tabbing (str)), map_title_parts[1], bounds_msg);
else -- map name same as en.wiki article title
str = string.format ('["%s"] = "%s",', k, qid, map_title_parts[1]);
str = string.format ('\t%s%s-- %s; %s', str, string.rep ('\t', tabbing (str)), map_title_parts[1], bounds_msg);
end
if invoked then -- when this function called through an invoke
str = str:gsub ('\t', ' ') -- replace plain-text tabs with html numeric entities
end
table.insert (temp_t, str); -- save
end
end
table.sort (temp_t, sort); -- ascending numerical sort by key (NTS id)
for i=#temp_t, 1, -1 do -- working backwards through the table
if temp_t[i]:match ('%d+%u"%]') then -- if this key is an area key (no sheet)
table.insert (temp_t, i, ''); -- insert an empty string to separate area-from-area
elseif i > 1 and temp_t[i]:match ('%["(%d+)') ~= temp_t[i-1]:match ('%["(%d+)') then -- because some series list don't begin with area maps (15 for example)
table.insert (temp_t, i, ''); -- insert an empty string to separate area-from-area
end
end
if invoked then -- when this function called through an invoke
return table.concat ({'
', table.concat (temp_t, ''}); -- concatenate into a big string using
'),'
tags and wrapped in
...tagsand done
else
return table.concat (temp_t, '\n'); -- concatenate into a big string using plain-text newlines and done
end
end
--[[--------------------------< N T S _ K E Y _ V A L I D A T E >----------------------------------------------
debug console function to makes sure that all keys in Module:Canada NTS/data have the correct form
=p.nts_key_validate()
]]
local function nts_key_validate()
for k, _ in pairs (data) do
local series, area, sheet = k:match ('^(%d+)(%u)(%d*)$');
if not series then
return 'invalid key: ' .. k .. '';
end
if not nts.nts_series_validate (tonumber(series)) then
return 'invalid series: ' .. k .. '';
end
if not nts.nts_area_validate (tonumber(series), area) then
return 'invalid area: ' .. k .. '';
end
if '' ~= sheet and not nts.nts_sheet_validate (tonumber(sheet)) then
return 'invalid sheet: ' .. k .. '';
end
end
return 'keys are valid';
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
main = main,
nts_key_validate = nts_key_validate,
}