require('strict'); -- alarm when global variables etc are used
local get_args = require ('Module:Arguments').getArgs; -- simplfy frame and parent frame argument fetching
--
local function wikidata_lat_lon_get local qid = mw.wikibase.getEntityIdForCurrentPage; -- get the qid for the current page 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, value_t.longitude; -- return coordinates from value_t end endend
--
local function article_name_from_qid_get (qid) local this_wiki_code = mw.language.getContentLanguage:getCode; -- get this wiki's language code if 'wikidata'
local wd_article = mw.wikibase.getSitelink (qid, this_wiki_code .. 'wiki'); -- fetch article title from WD; nil when no title available at this wiki
if wd_article then wd_article = table.concat ; -- interwiki-style link without brackets if taken from WD; leading colon required end
return wd_article; -- article title from WD; nil elseend
----------------------------< _ N A M E >--------------------------------------------------------------------
This function takes in a National Tiling System ID and outputs the name of the National Topographic System mapsheet with that ID. If no map sheet has been published under that ID, the output will be blank.
local function _name (args_t) local data_t = mw.loadData ('Module:Canada NTS/data'); -- load the ~/data module if not data_t[args_t[1]] then -- nil when NTS ID not listed in ~/data return ""; elseif
local title_parts_t = mw.text.split (data_t[args_t[1]], '|'); -- title_parts[1] is link part of a wikilink; title_parts[2] is wikilink label (map name) or nil if mw.title.getCurrentTitle.text
return string.format ('%s', data_t[args_t[1]]); -- return wikilinked map titleend
--name|
local function name (frame) local args_t = get_args (frame); if not args_t[1] then return '
National Tiling System ID required'; -- TODO: better, more informative error handling end return _name (args_t);end----------------------------< C O O R D _ L I M I T S _ T >--------------------------------------------------
a sequence of sequences. Each sequence in
local coord_limits_t =
----------------------------< L A T _ L O N _ V A L I D A T E >----------------------------------------------
validates
returns true when in bounds; false else
local function lat_lon_validate (lat, lon) for _, coord_limit_t in ipairs (coord_limits_t) do -- loop through the rectangle sequences in
if (lat >= lat_S) and (lat < lat_N) and (lon >= lon_E) and (lon < lon_W) then return true; --
--
local function extents (lat, lon) local belt = math.floor((lat - 40) * 4); local strip = math.floor((lon - 48) * 2);
local s, n, e, w -- 1:50,000 scale bounding box local a_s, a_n, a_e, a_w -- 1:250,000 scale bounding box
local lat_limits = -- Latitude limits of bounding box
local lon_limits = -- Calculation of longitude limits is different depending on zone if lat >= 40 and lat < 68 then -- Southern zone lon_limits["e"] = strip / 2 + 48; lon_limits["w"] = strip / 2 + 48.5; lon_limits["a_e"] = math.floor(strip / 4) * 2 + 48; lon_limits["a_w"] = math.floor(strip / 4) * 2 + 50;
elseif lat >= 68 and lat < 80 then -- Arctic zone lon_limits["e"] = math.floor(strip / 2) + 48; lon_limits["w"] = math.floor(strip / 2) + 49; lon_limits["a_e"] = math.floor(strip / 8) * 4 + 48; lon_limits["a_w"] = math.floor(strip / 8) * 4 + 52;
elseif lat >= 80 and lat < 88 then -- High Arctic zone lon_limits["e"] = math.floor(strip / 2) + 48; lon_limits["w"] = math.floor(strip / 2) + 49; lon_limits["a_e"] = math.floor(strip / 8) * 4 + 48; lon_limits["a_w"] = math.floor(strip / 8) * 4 + 52; end return end
----------------------------< N T S _ I D _ F R O M _ L A T _ L O N _ G E T >--------------------------------
calculates NTS identifier from latitude and longitude. If
when successful, returns NTS identifier; nil else
local function nts_id_from_lat_long_get (lat, lon) if not (lat and lon) then -- nil when missing, empty, or not a number lat, lon = wikidata_lat_lon_get; --
local series, numarea, area, sheet_inter, sheet if lat >= 40 and lat < 68 then -- Southern zone series = (math.floor((lon - 48) / 8) * 10) + math.floor((lat - 40) / 4) -- Calculate 1:1,000,000 map series ID numarea = tonumber(math.floor(((lat - 40) / 4) % 1 * 4) * 10 + math.floor(((lon - 48) / 8) % 1 * 4)) -- Calculate 1:250,000 map area ID local southern_zone_t = ; area = southern_zone_t[numarea]; -- translate sheet_inter = math.floor((lat % 1) * 4) * 10 + math.floor((((lon - 48) / 8) % 1 * 4) % 1 * 4) -- Calculate 1:50,000 map sheet ID
elseif lat >= 68 and lat < 80 then -- Arctic zone series = (math.floor((lon - 48) / 8) * 10) + math.floor((lat - 40) / 4) -- Calculate 1:1,000,000 map series ID numarea = (math.floor(lat % 4) * 10) + math.floor((lon / 4) % 2) -- Calculate 1:250,000 map area ID local arctic_zone_t = ; area = arctic_zone_t[numarea]; -- translate sheet_inter = math.floor((lat % 1) * 4) * 10 + math.floor(lon % 4) -- Calculate 1:50,000 map sheet ID
elseif lat >= 80 and lat < 88 then -- High Arctic zone if lon >= 56 and lon < 72 then -- Calculate 1:1,000,000 map series ID if lat >= 84 then series = 121 else series = 120 end -- These are correct - Go to
local sheet_t = sheet = sheet_t[sheet_inter]
return series, area, sheet, lat, lon; end
--
local function nts_series_validate (series) series = (series and tonumber (series)); -- convert series to a number if not series then return nil; -- something other than a number; declare failure and abandon end
local series_limits_t =
for _, limits_t in ipairs (series_limits_t) do -- loop through the series limits if (series >= limits_t[1]) and (series <= limits_t[2]) then -- is series within these limits? return series; -- yes, return series as a number end endend
--
local function nts_area_validate (series, area) local is_northern; -- a flag local northern_series_t = for _, limits_t in ipairs (northern_series_t) do if (series >= limits_t[1]) and (series <= limits_t[2]) then -- is series in arctic or high arctic? is_northern = true; -- yes, set the flag break; -- and go on to the next tests end end if is_northern then -- if arctic or high arctic series if not area:match ('^[A-H]$') then -- area must be a single uppercase letter in the range A-H return nil; -- out of bounds, declare failure and abandon end else -- here when southern series if not area:match ('^[A-P]$') then -- area must be a single uppercase letter in the range A-P return nil; -- out of bounds, declare failure and abandon end end
return area;end
--
local function nts_sheet_validate (sheet) sheet = (sheet and tonumber (sheet)) or nil; -- sheet as a number; or if not a number or not present, nil if not sheet then return nil; -- something other than a number; declare failure and abandon end
if (1 > sheet) or (16 < sheet) then -- must be a number in the range 1–16 return nil; -- out of bounds, declare failure and abandon end
return sheet; -- return sheet as a numberend
--
local function extents_from_grid (series, area, sheet) local belt -- 192 belts between 40°N and 88°N, each 0.25° of latitude in breadth local belt_area_south = local belt_area_north = local belt_sheet =
if series >= 120 then belt = 160 + series % 10 * 16 + (belt_area_north[area] or 0) + (belt_sheet[sheet] or 0) elseif series < 120 and series % 10 * 16 >= 112 then belt = series % 10 * 16 + (belt_area_north[area] or 0) + (belt_sheet[sheet] or 0) else belt = series % 10 * 16 + (belt_area_south[area] or 0) + (belt_sheet[sheet] or 0) end
local strip -- 192 belts between 48°W and 144°W, each 0.5° of longitude in breadth local strip_series_high_arctic = local strip_area_southern = local strip_area_arctic = local strip_area_high_arctic =
local strip_sheet_southern = local strip_sheet_arctic = local strip_sheet_high_arctic =
local east_limit, west_limit; -- For 1:50,000 scale map sheet local area_east_limit, area_west_limit; -- For 1:250,000 scale map area
if series >= 120 then -- High Arctic zone strip = strip_series_high_arctic[math.floor(series / 10)] + (strip_area_high_arctic[area] or 0) + (strip_sheet_high_arctic[sheet] or 0);
east_limit = strip * 0.5 + 48 west_limit = (strip + 4) * 0.5 + 48 area_east_limit = math.floor(strip / 16) * 8 + 48 area_west_limit = math.floor((strip + 16) / 16) * 8 + 48
elseif series < 120 and math.floor(series % 10) >= 7 then -- Arctic zone strip = math.floor(series / 10) * 16 + (strip_area_arctic[area] or 0) + (strip_sheet_arctic[sheet] or 0);
east_limit = strip * 0.5 + 48 west_limit = (strip + 2) * 0.5 + 48 area_east_limit = math.floor(strip / 8) * 4 + 48 area_west_limit = math.floor((strip + 8) / 8) * 4 + 48
else -- Southern zone strip = math.floor(series / 10) * 16 + (strip_area_southern[area] or 0) + (strip_sheet_southern[sheet] or 0);
east_limit = strip * 0.5 + 48 west_limit = (strip + 1) * 0.5 + 48 area_east_limit = math.floor(strip / 4) * 2 + 48 area_west_limit = math.floor((strip + 4) / 4) * 2 + 48
end local grid_limits = return grid_limitsend
--grid|link=yes}} -- for now, |link=yes is required
This function takes a Canadian National Tiling System map sheet ID, or latitude and longitude either as input orfrom the wikidata qid of the current page and creates a url for the map sheet. Latitude and longitude are indecimal degrees, and automatically assumed to be north and west, as no Canadian territory lies in the southernor eastern hemispheres.
-- For 1:50,000 scale map sheet ID, using coordinates from current article's Wikidata entry -- For 1:50,000 scale map sheet ID, using NTS ID (see Template:Canada NTS Map Sheet) -- For 1:50,000 scale map sheet ID, using coordinates specified in argument
optional parameters: |area= to obtain a 1:250,000 scale map area ID instead |link= to create an appropriate link to the Canadian government's Geospatial Data Extraction tool |name= to append the map name (from Module:Canada_NTS/data) to the output
local function grid (frame) local args_t = get_args (frame); -- fetch frame and parent frame parameters into a single table local lat, lon = tonumber(args_t.lat), tonumber(args_t.lon); -- flags to control what the output looks like local print_area = 'yes'
args_t.link; -- when true, format the area or sheet output as an external link local print_name = 'yes'
local series, area, sheet; for k, v in ipairs (args_t) do if 1
elseif 2
elseif 3
if series and not sheet then -- if we have nts id without sheet (an area-only) print_area = true; -- set this so that later we create the correct output end if not series or not area then series, area, sheet, lat, lon = nts_id_from_lat_long_get (lat, lon); if not series then return '
lat/long input fail'; -- TODO: better, more informative error handling end endlocal output = print_area and (series .. area) or (series .. area .. sheet); print_name = print_name and (' ' .. _name) or ; -- reuse
local extents_t = ; -- to hold bounding box coordinates for url if print_link and lat then -- when we have lat/lon extents_t = extents (lat, lon); -- get a table of sheet and area extents from lat/lon elseif print_link then -- when we have nts id extents_t = extents_from_grid (series, area, sheet); -- get a table of sheet and area extents from nts id parts if extents_t
if print_link then local ext_link_fmt_str = '%s'; output = print_area and string.format (ext_link_fmt_str, extents_t.area_west, extents_t.area_south, extents_t.area_east, extents_t.area_north, output, output) or string.format (ext_link_fmt_str, extents_t.west, extents_t.south, extents_t.east, extents_t.north, output, output); end
-- output = output .. print_name; -- append name or empty string-- return output; -- and done
local place_name = mw.title.getCurrentTitle.text; -- where does this come from? article title? local nts = series .. area .. (sheet or ); local center_lat, center_lon; -- center point of NTS map
local polygon_t = ; local north, west, south, east;
if sheet then north = extents_t.north; west = extents_t.west; south = extents_t.south; east = extents_t.east else north = extents_t.area_north; west = extents_t.area_west; south = extents_t.area_south; east = extents_t.area_east end
center_lat = (north + south) / 2.0; -- calculate map center point center_lon = (west + east) / 2.0; for _, v_t in ipairs do table.insert (polygon_t, string.format ('[-%s, %s]', v_t[1], v_t[2])); -- make longitudes negative here end
local map_frame_tag_open = string.format ('
local feature_collection = '', feature_collection);
return frame:preprocess (map_frame_tag_open .. feature_collection .. map_frame_tag_close)end
----------------------------< D O C _ S U P P O R T >--------------------------------------------------------
local function doc_support (frame) local args_t = get_args (frame); -- fetch frame and parent frame parameters into a single table local data = mw.loadData ('Module:Canada NTS/data'); -- load the ~/data module local lang_obj = mw.language.getContentLanguage; -- get language object for number formatting local count = 0; -- a generic counter local area_count = 0; -- counter for area maps (1:250,000) local sheet_count = 0; -- counter for sheet maps (1:50,000) if 'count'
args_t[1] then -- count the number of entries in ~/data that do not have a NTS title for _, v in pairs (data) do -- don't care about key if
return lang_obj:formatNum(count); -- make all pretty-like and doneend
----------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
return