Module:Cyclone map explained

require('strict')local fn = require('Module:Formatnum')--local mm = require('Module:Math')local Date = require('Module:Date')._Datelocal p =

-- N/A but possible option: keep cyclone data in subpage data files--local stormDatabase = require("Module:Cyclone map/data") -- configuration module -- main function callable in Wikipedia via the #invoke command.p.main = function(frame) local str = p.getMapframeString return frame:preprocess(str) -- the mapframe needs to be preprocessed!!!!!end -- End the function.

--function to construct mapframe string sets up the tags MAPDATA in form of geojson constucted with function getGeoJSON --p.getMapframeString = function(frame)

--get mapframe arguments from calling templates local parent = mw.getCurrentFrame:getParent -- get JSON data for features to display local mapData = p.getGeoJSON local mapString = ""

--mapString = '' if mapData ~= "" then

mapString = '

mapString = mapString .. ' width=' .. math.floor(width) .. ' height=' .. math.floor(height) .. ' align=' .. align

local zoom = parent.args['zoom'] --or "0" -- no longer set defaults (mapframe does automatically) local latitude = parent.args['latitude'] --or "0" local longitude = parent.args['longitude'] --or "0" --set if values, otherwise allow mapframe to set automatically (TODO check if longitude and latitude are independent) if zoom then mapString = mapString .. ' zoom=' .. zoom end if latitude then mapString = mapString .. ' latitude=' .. latitude end if longitude then mapString = mapString .. ' longitude=' .. longitude end mapString = mapString .. ' >' .. mapData .. '' -- add data and close tag else mapString = "No data for map" end return mapString

end -- End the function.

--imageN=, |descriptionN=) (2) from values in the data module (i.e. Module:Football map/data) [not function for cyclone map] (3) from Wikidatap.getGeoJSON = function(frame)

-- now we need to iterate through the stadiumN parameters and get data for the feature markers local maxNumber = 200 -- maximum number looked for local mapData = "" local cycloneName = nil local cycloneID = nil --get mapframe arguments from calling templates local parent = mw.getCurrentFrame:getParent --There are three ways of getting data about the stadium features (1) from a list in the module subpages (n/a but possible alternative) (2) from wikidata (3) from the parameters in the template (these always override other) The parameters useWikiData, useModule restrict use of source -- local useWikidata = true local useModule = false if parent.args['wikidata'] then useWikidata = true; useModule = false end -- use wikidata or template data (no module data) if parent.args['moduledata'] then useModule = true; useWikidata = false end -- use module of template data (no wikidata) if parent.args['templatedata'] then useModule = false; useWikidata = false end -- only use template data -- default parameters for marker color, size and symbol, etc (i.e. those without index suffix) local strokeColor = parent.args['stroke'] or "#000000" if strokeColor

"auto" then strokeColor = nil end -- if using auto color -- the default properties are set by the unindexed parameters and affect all objects local defaultProperties =

local index=0 while index < maxNumber do index = index + 1 local cycloneID = "" -- (1) get cyclone name cycloneID = parent.args['id'..tostring(index)] cycloneName = parent.args['name'..tostring(index)] if cycloneName and not cycloneID then cycloneID = mw.wikibase.getEntityIdForTitle(cycloneName) end if cycloneID and not cycloneName then cycloneName = mw.wikibase.getLabel(cycloneID) --TODO get associated Wikipedia page for linking end -- if we have a valid cyclone id (note:Lua has no continue statement) if cycloneID then local feature = local validFeatureData = true -- assume now -- (2) get feature parameters from module (n/a) or wikidata or both --if useModule then -- get feature parameters from module data stadium list feature = p.getModuleData(frame, stadiumName) end if useWikidata and cycloneID then --and feature['name']

"" then -- get feature parameters from wikidata feature = p.getDataFromWikiData(cycloneName,cycloneID) if not feature['valid'] then -- no valid coordinates validFeatureData =false mw.addWarning("No valid coordinates found for " .. cycloneName .. " (" .. cycloneID .. ")") end end ---------------------------------------------------- -- (3) data from template parameters will override those obtainied from a module table or wikidata local templateArgs = if templateArgs['latitude'] and templateArgs['longitude'] then -- if both explicitly set by template feature['latitude'] = templateArgs['latitude'] feature['longitude']= templateArgs['longitude'] feature['name'] = cycloneName -- as we have valid coordinates validFeatureData =true end -- use specified description and image if provided if templateArgs['description'] then feature['description'] = templateArgs['description'] end if templateArgs['image'] then feature['image'] = templateArgs['image'] -- priority for image from template argument end if feature['image'] ~= "" then feature['image'] = '' .. feature['image'] .. '' end -- wikilink - use redirect if alias if feature['alias'] ~= then feature['name'] = ''.. feature['alias'] .. '' else feature['name'] = '' .. feature['name'] .. '' end

if feature['image'] ~= "" then feature['description'] = feature['image'] .. feature['description'] end

--check if current feature marker has specified color, size or symbol local strokeColor = parent.args['stroke'..tostring(index)] or defaultProperties['stroke'] if strokeColor

"auto" then strokeColor = nil end -- if using auto color

-- the feature properties are set by the indexed parameters or defaults (see above) local featureProperties = --(4) construct the json for the features (if we have a storm with valid coordinates) if validFeatureData then local featureData = ""

if feature.path[1] then -- add path if multiple coordinates featureData = p.addPathFeatureCollection(feature,featureProperties) else -- else show single marker -- make sure a marker color is set (if not set by template or not autocoloring storm path) -- note the default colour is left as nil for the auto coloring of paths by storm type -- and that this can be overriden with a value, but might be nil here local markerColor = featureProperties['marker-color'] or "#0050d0" featureData = ' ' end if index > 1 and mapData ~= "" then mapData = mapData .. ',' .. featureData else mapData = featureData end else --mapData = ' ' mw.addWarning("No valid information found for " .. cycloneName .. " (" .. cycloneID .. ")") end -- if valid parameters end -- end if if cycloneID end -- end while loop --(5) check for external data (geoshape) TODO add more than index=1 and generalise for any json feature -- local geoshape = parent.args['geoshape'..tostring(1)] or "" if geoshape ~= "" then mapData = mapData .. ',' .. geoshape -- assumes at least one stadium end -- add outer bracket to json if more than one element if index > 1 then mapData = '[' .. mapData .. ']' --mapData = ' ' -- is there an advantage using this? end return mapData end -- End the function.

--functions adding path to cyclone item p.addPathFeatureCollection -- adds markers/symbols and lines for path of storm (cordinates from wikidata) p.addShapeFeature -- returns geoJson for the custom symbol p.getPolygonCoordinates -- returns coordinate set for the custom symbol (loop for diffent shapes) p.calculatePolygonCoordinates -- calculates the coordinates for the specified shape p.getCycloneColor -- sets color of symbols/lines based on storm type (from wikidata)

--

p.addPathFeatureCollection=function(feature, featureProperties) if not feature.path[1] then return "" end -- shouldn't be necessary now local mode = mw.getCurrentFrame:getParent.args['mode'] or "default"

local featureCollection = "" local sep = "" local i = 1 table.sort (feature.path, function(a,b) if (a.timeStamp < b.timeStamp) then --- primary sort on timeStamp return true else return false end end) for i, v in pairs(feature.path) do local autoColor = p.getCycloneColor(feature.path[i]['cycloneType'], featureProperties) local markerColor = featureProperties['marker-color'] or autoColor local strokeColor = featureProperties['stroke'] or autoColor local longitude = feature.path[i]['longitude'] local latitude = feature.path[i]['latitude']

-- add a lines between the points (current point to the next point, if there is one) local lineFeature = "" if feature.path[i+1] then local longitude2 = feature.path[i+1]['longitude'] local latitude2 = feature.path[i+1]['latitude']

lineFeature = ' ' featureCollection = featureCollection .. sep .. lineFeature sep = "," end

-- if mode

"marker" or (mode

"test" and i

1) then

local pointFeature = ' ' featureCollection = featureCollection .. sep .. pointFeature sep = ","

elseif mode

"test" then -- short lines (test) to mark with objects local dateString = " 2020-06" if feature.path[i]['timeStamp'] then local formattedDate = Date(feature.path[i]['timeStamp']):text("dmy hm") dateString = '
date and time: ' .. formattedDate --tostring(feature.path[i]['timeStamp']) end local description = '

latitude: ' .. tostring(latitude) .. '
longitude: ' .. tostring(longitude) .. dateString .. '

' local circleFeature = ' ' featureCollection = featureCollection .. sep .. circleFeature sep = "," else -- use polygons (default if not marker) to mark with objects

featureCollection = featureCollection .. sep .. p.addShapeFeature(i, feature, featureProperties) sep = "," end i=i+1 -- increment for next point in storm path end -- while/for in pairs if mw.getCurrentFrame:getParent.args['mode']

"test3" then featureCollection = '' end --return sep .. featureCollection return featureCollectionend--p.addShapeFeature =function(i, feature, featureProperties) local size = featureProperties['symbol-size'] local shape = featureProperties['symbol-shape'] -- symbol for tropical cyclone if feature.path[i]['cycloneType2']

'extratropical cyclone' then shape = 'triangle' -- symbol for extratropical cyclone (Q1063457) elseif feature.path[i]['cycloneType2']

'subtropical cyclone' then shape = 'square' -- symbol for subtropical cyclone (Q2331851) end local autoColor = p.getCycloneColor(feature.path[i]['cycloneType'], featureProperties) --local markerColor = featureProperties['symbol-color'] or autoColor local strokeColor = featureProperties['symbol-stroke'] or autoColor local fillColor = featureProperties['symbol-fill'] or autoColor local longitude = feature.path[i]['longitude'] local latitude = feature.path[i]['latitude']

local description = '

' -- .. '
date: ' .. mw.language.getContentLanguage:formatDate('d F Y', feature.path[i]['timeStamp']) .. '
date: ' .. Date(feature.path[i]['timeStamp']):text("dmy") -- :text("dmy hm") .. '
type: ' .. tostring(feature.path[i]['cycloneType']) .. '
longitude: ' .. fn.formatNum(longitude,"en",6) .. '
latitude: ' .. fn.formatNum(latitude,"en",6) .. '

' local shapeFeature ="" --shape="circle" shapeFeature = ' ' -- if shape

cyclone, a circle shape will have been drawn; now add the tails if shape

"cyclone" then -- superimpose a second shape local shape2="cyclone_tails" shapeFeature = shapeFeature .. ', ' .. ' ' end return shapeFeatureendp.getPolygonCoordinates = function(shape, size, latitude, longitude) -- shape = "circle" -- shape ="spiral" local coordinates = "" if shape

"square" then coordinates = ' [' .. '[' .. (longitude+size) .. ',' .. (latitude+size) .. '],' .. '[' .. (longitude+size) .. ',' .. (latitude-size) .. '],' .. '[' .. (longitude-size) .. ',' .. (latitude-size) .. '],' .. '[' .. (longitude-size) .. ',' .. (latitude+size) .. '],' .. '[' .. (longitude+size) .. ',' .. (latitude+size) .. ']' .. '] ' elseif shape

"triangle2" then coordinates = ' [' .. '[' .. (longitude) .. ',' .. (latitude+size) .. '],' .. '[' .. (longitude+size) .. ',' .. (latitude-size) .. '],' .. '[' .. (longitude-size) .. ',' .. (latitude-size) .. '],' .. '[' .. (longitude) .. ',' .. (latitude+size) .. ']' .. '] ' elseif shape

"inverse-triangle" then coordinates = ' [' .. '[' .. (longitude) .. ',' .. (latitude-size) .. '],' .. '[' .. (longitude+size) .. ',' .. (latitude+size) .. '],' .. '[' .. (longitude-size) .. ',' .. (latitude+size) .. '],' .. '[' .. (longitude) .. ',' .. (latitude-size) .. ']' .. '] ' elseif shape

"star2" then --size = size * 5 coordinates = ' [' .. '[' .. longitude .. ',' .. latitude+(size*1.2) .. '],' -- top point .. '[' .. longitude+(size*0.2) .. ',' .. latitude+(size*0.2) .. '],' .. '[' .. longitude+(size*1.2) .. ',' .. latitude+(size*0.4) .. '],' -- 2pm point .. '[' .. longitude+(size*0.3) .. ',' .. latitude-(size*0.1) .. '],' .. '[' .. longitude+(size) .. ',' .. latitude-(size) .. '],' -- 5pm point .. '[' .. longitude .. ',' .. latitude-(size*0.3) .. '],' -- 6pm (innner) .. '[' .. longitude-(size) .. ',' .. latitude-(size) .. '],' -- 7pm point .. '[' .. longitude-(size*0.3) .. ',' .. latitude-(size*0.1) .. '],' .. '[' .. longitude-(size*1.2) .. ',' .. latitude+(size*0.4) .. '],' -- 10pm point .. '[' .. longitude-(size*0.2) .. ',' .. latitude+(size*0.2) .. '],' .. '[' .. longitude .. ',' .. latitude+(size*1.2) .. ']' -- top point (close) .. '] '

elseif shape

"circle2" then local radius = size coordinates = coordinates .. ' [' for angle = 0, 360, 3 do if angle > 0 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude +(radius*math.cos(math.rad(angle))) .. ',' .. latitude +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] ' elseif shape

"cyclone_tails" then

local radius = size*2 -- add tail at 3 o'clock coordinates = coordinates .. ' [' for angle = 0, 60, 3 do if angle > 0 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude-size+(radius*math.cos(math.rad(angle))) .. ',' .. latitude +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] '

-- add tail at 9 o'clock coordinates = coordinates .. ', [' for angle = 180, 240, 3 do if angle > 180 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude+size+(radius*math.cos(math.rad(angle))) .. ',' .. latitude +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] ' -- add tail at 6 o'clock coordinates = coordinates .. ', [' for angle = 270, 330, 3 do if angle > 270 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude+(radius*math.cos(math.rad(angle))) .. ',' .. latitude+size +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] '

-- add tail at 6 o'clock coordinates = coordinates .. ', [' for angle = 90, 150, 3 do if angle > 90 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude+(radius*math.cos(math.rad(angle))) .. ',' .. latitude-size +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] ' --for adding circle local radius = size coordinates = coordinates .. ', [' for angle = 0, 360, 3 do if angle > 0 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude+(radius*math.cos(math.rad(angle))) .. ',' .. latitude +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] ' -- elseif shape

"spiral" then

coordinates = ' [' local radius = size*0.01 for angle = 0, 360*4, 4 do radius = radius + size*0.01 if angle > 0 then coordinates = coordinates .. ',' end coordinates = coordinates .. '[' .. longitude+(radius*math.cos(math.rad(angle))) .. ',' .. latitude +(radius*math.sin(math.rad(angle))) .. ']' end coordinates = coordinates .. '] ' elseif shape

"circle" or shape

"cyclone" then -- circle as 120 sided polygon return p.calculatePolygonCoordinates(120, 1, size, latitude, longitude) elseif shape

"triangle" then return p.calculatePolygonCoordinates(3, 1, size, latitude, longitude) elseif shape

"diamond" then return p.calculatePolygonCoordinates(4, 1, size, latitude, longitude) elseif shape

"hexagon" then return p.calculatePolygonCoordinates(6, 1, size, latitude, longitude) elseif shape

"octagon" then return p.calculatePolygonCoordinates(8, 1, size, latitude, longitude) elseif shape

"star4" then return p.calculatePolygonCoordinates(8, 3, size, latitude, longitude) elseif shape

"star5" then return p.calculatePolygonCoordinates(10, 3, size, latitude, longitude) elseif shape

"star8" then return p.calculatePolygonCoordinates(16, 3, size, latitude, longitude) elseif shape

"star12" then return p.calculatePolygonCoordinates(24, 3, size, latitude, longitude) elseif shape

"star" then return p.calculatePolygonCoordinates(10, 2, size, latitude, longitude) end return coordinatesend--p.calculatePolygonCoordinates = function(sides, ratio, size, latitude, longitude) local coordinates = ' [' local outer = true local radius = size for angle = 0, 360, 360/sides do if angle > 0 then coordinates = coordinates .. ',' end -- important for geojson structure (unlike Lua) if radius ~= 1 then -- if a star if outer then radius = size else radius = size/ratio end -- alternate inner and outer radius outer = not outer end coordinates = coordinates .. '[' .. longitude+(radius*math.sin(math.rad(angle))) .. ',' .. latitude +(radius*math.cos(math.rad(angle))) .. ']' end return coordinates .. '] ' end--p.getCycloneColor=function(cycloneType, featureProperties) sets color of symbols/lines based on storm type (from wikidata)p.getCycloneColor=function(cycloneType, featureProperties) --codors from "Tropical cyclone scales" article #80ccff Depression, Zone of Disturbed Weather, Tropical Disturbance (?) #5ebaff Tropical Depression, Deep Depression Tropical Disturbance (?), Tropical Depression, Tropical Low #00faf4 tropical storm, moderate tropical storm, cyclonic storm, Category 1 Tropical Cyclone #ccffff severe cyclonic storm, severe tropical storm, Category 1 Hurricane #ffffcc Very Severe Cyclonic Storm Tropical Cyclone #fdaf9a Typhoon #ffc140 Very Strong Typhoon, Extremely Severe Cyclonic Storm, Intense Tropical Cyclone Category 4 #ff6060 Violent Typhoon, Category 5 Severe Tropical Cyclone, Super Typhoon, Super Cyclonic Storm, Very Intense Tropical Cyclone, Category 5 Major Hurricane local color = "#000000" cycloneType = string.lower(cycloneType) if cycloneType

"depression" or cycloneType

"zone of disturbed weather" or cycloneType

"Tropical disturbance" then color = "#80ccff" elseif cycloneType

"tropical depression" or cycloneType

"deep depression" or cycloneType

"tropical Depression" or cycloneType

"tropical low" then color = "#5ebaff" elseif cycloneType

"cyclonic storm" or cycloneType

"tropical storm" or cycloneType

"moderate tropical storm" or cycloneType

"category 1 tropical cyclone" then color = "#00faf4" elseif cycloneType

"severe cyclonic storm" or cycloneType

"severe tropical storm" or cycloneType

"category 1 hurricane" then color = "#ccffff" elseif cycloneType

"very severe cyclonic storm" or cycloneType

"tropical cyclone" then color = "#ffffcc" elseif cycloneType

"typhoon" then color = "#fdaf9a" elseif cycloneType

"very strong typhoon" or cycloneType

"extremely severe cyclonic storm" or cycloneType

"intense tropical cyclone" or cycloneType

"category 4 severe tropical storm" then color = "#ffc140" elseif cycloneType

"violent typhoon" or cycloneType

"category 5 severe tropical cyclone" or cycloneType

"super typhoon" or cycloneType

"super cyclonic storm" or cycloneType

"very intense tropical cyclone" or cycloneType

"category 5 major hurricane" then color = "#ff6060" end return colorend

--