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 ; nil else

]]

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  is empty string 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  is empty string 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 same as or north of and is same as or south of ?

returns direction 'north' or 'south' when out of bounds; nil else

]]

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 same as or east of and is same as or west of ?

returns direction 'east' or 'west' when out of bounds; nil else

]]

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 and are out of bounds, this function returns an error 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  entries from Module:Canada NTS/data. For each of those, attempt to get qid that matches the 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')

]]

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', '&#9;')									-- 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, }