Module:Cyclone map
Appearance
Implements {{Cyclone map}}
require('Module:No globals')
local p = {}
local stadiumDatabase = require( "Module:Football 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
--]]
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.getJSON()
local mapString = ""
--mapString = '<mapframe text="London football stadia" width=800 height=650 align=left zoom=11 latitude=51.530 longitude=-0.16 >'
if mapData ~= "" then
mapString = '<mapframe'
if parent.args['frameless'] then -- don't and text as this overrides frameless parameter
mapString = mapString .. ' frameless'
else
mapString = mapString .. ' text="' .. (parent.args['text'] or "") .. '"'
end
mapString = mapString
.. ' width=' .. (parent.args['width'] or "400" )
.. ' height=' .. (parent.args['height'] or "300")
.. ' align=' .. (parent.args['align'] or "right")
.. ' zoom=' .. (parent.args['zoom'] or "9" )
.. ' latitude=' .. (parent.args['latitude'] or "51.5")
.. ' longitude=' .. (parent.args['longitude'] or "-0.15")
.. ' >'
.. mapData
.. '</mapframe>'
else
mapString = "No data for map"
end
return mapString
end -- End the function.
--[[ function to construct JSON format data for markers on map.
The information for each marker (coordinate, description and image for popup, etc)
can be set in several ways (in order of priority):
(1) using arguments in the template (|imageN=, |descriptionN=)
(2) from values in the data module (i.e. Module:Football map/data)
(3) from Wikidata
]]
p.getJSON = 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 defaultProperties = {
['marker-color'] = parent.args['marker-color'], -- default to nil --or "#0050d0",
['marker-size'] = parent.args['marker-size'] or "small",
['marker-symbol'] = parent.args['marker-symbol'] or "",
['stroke'] = parent.args['stroke'], -- nil default causes autocolor path; a value overrides autocolor
['stroke-width'] = parent.args['stroke-width'] or 3,
['stroke-opacity'] = parent.args['stroke-opacity'] or 1.0
}
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 = {name="",alias="",latitude=0,longitude=0,description="",image="",valid=false, path={} }
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 = {
latitude = parent.args['latitude'..tostring(index)],
longitude = parent.args['longitude'..tostring(index)],
description = parent.args['description'..tostring(index)],
image = parent.args['image'..tostring(index)]
}
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['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 featureProperties = {
['marker-color'] = parent.args['marker-color'..tostring(index)] or defaultProperties['marker-color'],
['marker-symbol'] = parent.args['marker-symbol'..tostring(index)] or defaultProperties['marker-symbol'],
['marker-size'] = parent.args['marker-size'..tostring(index)] or defaultProperties['marker-size'],
['stroke'] = parent.args['stroke'..tostring(index)] or defaultProperties['stroke'],
['stroke-width'] = parent.args['stroke-width'..tostring(index)] or defaultProperties['stroke-width'],
['stroke-opacity'] = parent.args['stroke-opacity'..tostring(index)] or defaultProperties['stroke-opacity']
}
--(4) construct the json for the features (if we have a storm with valid coordinates)
if validFeatureData then
local markerColor = featureProperties['marker-color'] or "#0050d0"
local featureData = ""
if feature.path[1] then -- add path if multiple coordinates
featureData = p.addPathFeatureCollection(feature,featureProperties)
else -- else show single marker
featureData = '{ "type": "Feature", '
.. ' "geometry": { "type": "Point", "coordinates": ['
.. feature['longitude'] .. ','
.. feature['latitude']
.. '] }, '
.. ' "properties": { "title": "' .. feature['name'] .. '", '
.. '"description": "' .. feature['description'] ..'", '
.. '"marker-symbol": "' .. featureProperties['marker-symbol'] .. '", '
.. '"marker-size": "' .. featureProperties['marker-size'] .. '", '
.. '"marker-color": "' .. markerColor .. '" } '
.. ' } '
end
if index > 1 and mapData ~= "" then
mapData = mapData .. ',' .. featureData
else
mapData = featureData
end
else
--mapData = '{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-0.066417, 51.60475] }, "properties": { "title": "White Hart Lane (default)", "description": "Stadium of Tottenham Hotspur F.C.", "marker-symbol": "soccer", "marker-size": "large", "marker-color": "0050d0" } } '
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 .. ']'
end
return mapData
end -- End the function.
--[[ functions adding path to cyclone item
function p.getCycloneColor() - sets color of symbols/lines based on storm type (from wikidata)
function p.addPathFeatureCollection - adds symbols/lines for path of storm (cordinates from wikidata)
--]]
p.getCycloneColor=function(cycloneType, featureProperties)
local color = "#000000"
if cycloneType == "depression" then color = "#0000ff"
elseif cycloneType == "deep depression" then color = "#00ffff"
elseif cycloneType == "Cyclonic storm" then color = "#ff8800" -- dark orange #FF8C00, orange #FFA500
elseif cycloneType == "Severe Cyclonic Storm" then color = "#ff0000"
end
return color
end
p.addPathFeatureCollection=function(feature, featureProperties)
if not feature.path[1] then return "" end
local featureCollection = ""
local sep = ""
local i = 1
while feature.path[i] do
local markerColor = featureProperties['marker-color'] or p.getCycloneColor( feature.path[i]['cycloneType'], featureProperties )
local strokeColor = featureProperties['stroke'] or p.getCycloneColor( feature.path[i]['cycloneType'], featureProperties )
local longitude = feature.path[i]['longitude']
local latitude = feature.path[i]['latitude']
-- place marker symbol on each point
if mw.getCurrentFrame():getParent().args['mode'] ~= "test" then
local pointFeature = '{ "type": "Feature", '
.. ' "geometry": { "type": "Point", "coordinates": [' .. longitude .. ',' .. latitude .. '] }, '
.. ' "properties": { "title": "' .. feature['name'] .. '", '
.. '"description": "' .. feature['description'] ..'", '
.. '"marker-symbol": "' .. featureProperties['marker-symbol'] .. '", '
.. '"marker-size": "' .. featureProperties['marker-size'] .. '", '
.. '"marker-color": "' .. markerColor .. '" } '
.. ' } '
featureCollection = featureCollection .. sep .. pointFeature
sep = ","
end
-- use short lines or polygons to mark with circle
if mw.getCurrentFrame():getParent().args['mode'] == "test" then
local description = '<div>latitude: ' .. tostring(latitude) .. '<br/>longitude: ' .. tostring(longitude) .. '</div>'
local circleFeature = '{ "type": "Feature", '
.. ' "geometry": { "type": "Polygon", "coordinates": [ ['
.. '[' .. (longitude+0.001) .. ',' .. (latitude+0.001) .. '],'
.. '[' .. (longitude+0.001) .. ',' .. (latitude-0.001) .. '],'
.. '[' .. (longitude-0.001) .. ',' .. (latitude-0.001) .. '],'
.. '[' .. (longitude-0.001) .. ',' .. (latitude+0.001) .. '],'
.. '[' .. (longitude+0.001) .. ',' .. (latitude+0.001) .. ']'
.. '] ] }, '
.. ' "properties": { "stroke": "' .. strokeColor .. '" , '
.. ' "fill": "' .. strokeColor .. '" ,'
.. ' "stroke-width": 10, ' -- .. featureProperties['stroke-width']
.. ' "description": "' .. description .. '"'
.. ' } '
.. ' } '
featureCollection = featureCollection .. sep .. circleFeature
sep = ","
elseif mw.getCurrentFrame():getParent().args['mode'] == "test2" then
local description = '<div>latitude: ' .. tostring(latitude) .. '<br/>longitude: ' .. tostring(longitude) .. '</div>'
local circleFeature = '{ "type": "Feature", '
.. ' "geometry": { "type": "LineString", "coordinates": ['
.. '[' .. longitude .. ',' .. latitude .. '],'
.. '[' .. (longitude+0.01) .. ',' .. (latitude+0.01) .. ']'
.. '] }, '
.. ' "properties": { "stroke": "' .. color .. '" , '
.. ' "stroke-width": 10, ' -- .. featureMarker['stroke-width']
.. ' "description": "' .. description .. '"'
.. ' } '
.. ' } '
featureCollection = featureCollection .. sep .. circleFeature
sep = ","
end
-- 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 = '{ "type": "Feature", '
.. ' "geometry": { "type": "LineString", "coordinates": ['
.. '[' .. longitude .. ',' .. latitude .. '],'
.. '[' .. longitude2 .. ',' .. latitude2 .. ']'
.. '] }, '
.. ' "properties": { "stroke": "' .. strokeColor .. '" , '
.. ' "stroke-width": ' .. featureProperties['stroke-width'] .. ' , '
.. ' "stroke-opacity": ' .. featureProperties['stroke-opacity']
.. ' } '
.. ' } '
featureCollection = featureCollection .. sep .. lineFeature
end
i=i+1
sep = ","
end -- while
if mw.getCurrentFrame():getParent().args['mode'] == "test" then
featureCollection =
'{"type": "FeatureCollection", "features": [ {"type": "Feature","properties": {"marker-color": "#bbccff", "marker-symbol": "-number"}, "geometry": { "type": "Point", "coordinates": [80.298888888889,6.3316666666667]}}, {"type": "Feature","properties": {"marker-color": "#bbccff", "marker-symbol": "-number"}, "geometry": { "type": "Point", "coordinates": [80.263888888889,6.6644444444444]}}, {"type": "Feature","properties": {"marker-color": "#bbccff", "marker-symbol": "-number"}, "geometry": { "type": "Point", "coordinates": [80.434444444444,7.1883333333333]}}, {"type": "Feature","properties": {"marker-color": "#bbccff", "marker-symbol": "-number"}, "geometry": { "type": "Point", "coordinates": [80.656111111111,8.1086111111111]}}, {"type": "Feature","properties": {"marker-color": "#bbccff", "marker-symbol": "-number"}, "geometry": { "type": "Point", "coordinates": [80.9025,8.2961111111111]}}, {"type":"Feature", "properties": { "stroke":"#D3D3D3", "stroke-opacity":0.7, "stroke-width":50}, "geometry": {"type":"LineString", "coordinates": [[80.298888888889,6.3316666666667],[80.263888888889,6.6644444444444],[80.434444444444,7.1883333333333],[80.656111111111,8.1086111111111],[80.9025,8.2961111111111]]}} ]}'
end
--return sep .. featureCollection
return featureCollection
end
--[[-------------------------------Retrieve information from wikidata-------------------------
statements of interest (datavalue element)
item = mw.wikibase.getEntity(WikidataId),
statements = item:getBestStatements('P625')[1]
"claims":
P625 coordinate location (value.longitude/latitude)
"P625":[{ "mainsnake": { ... "datavalue": { "value": {"latitude": 51.4, "longitude": -0.19] ...
statements.mainsnak.datavalue.value.latitude
P18 image on commons (value, "File:value")
"P18":[{ "mainsnake": { ... "datavalue": { "value": "Stamford Bridge Clear Skies.JPG"
P466 occupant (value.id) (use )
P1083 capacity (value.amount)
"P1083":[{ "mainsnake": { ... "datavalue": { "value": { "amount" : "+41875" ...
P571 inception (value), P576 demolished (value)
P1566 GeoNames ID (value, "geonames.org/value")
P84 architect
P137 operator, P127 owned by
P31 (instance of) Q483110 (stadium)
"P18":[{ "mainsnake": { ... "datavalue": { "value": { "id": "Q483110"
however also sports venue, olympic stadium, association football stadium
P159 headquarters location (for football club)
e..g. London
qualifier property: coordinates(P625)
page title on enwiki
mw.wikibase.getSitelink( itemId ) - gets local version
"sitelink": { "enwiki": { "title": "Stamford Bridge (stadium)"
ERROR NOTE there was an error is caused when a supposed stadium redirected to page with no coordinates
e.g Fortress Stadium, Bromley was redirecting to Bromley F.C.,
this had a valid Wiki ID and item but no coordinates
1. it is handled by setting wd['valid'] when there are valid coordinates
2. an alternative would We could check it is a stadium
if P31 (instance of ) Q483110 (stadium)
--]]
p.getDataFromWikiData=function(cycloneName,cycloneID)
local wd={name="",latitude="",longitude="",description="",image="",alias="",valid=false, path = {} }
-- get wikidata id corresponding to wikipedia stadium page
--local WikidataId = mw.wikibase.getEntityIdForTitle(cycloneName)
local WikidataId = cycloneID
if not cycloneName then cycloneName = "unnamed" end --TODO get the name
if WikidataId and mw.wikibase.isValidEntityId( WikidataId ) then -- valid id
local item = mw.wikibase.getEntity(WikidataId)
if not item then return wd end -- will test for wiki
local enwikiTitle = mw.wikibase.getSitelink( WikidataId ) -- name of local Wikipedia page
local wikidataTitle = mw.wikibase.getLabel( WikidataId ) -- name of Wikidata page
if enwikiTitle and wikidataTitle and enwikiTitle ~= wikidataTitle then
wd['alias'] = wikidataTitle
wd['name'] =cycloneName
else
wd['name'] =cycloneName
end
-- get coordinates
local statements = item:getBestStatements('P625')[1] --coordinate location
if statements ~= nil then -- check cordinates available
local coord = statements.mainsnak.datavalue.value
if type(coord.latitude) == 'number' and type(coord.longitude) == 'number' then
-- add coordinate data from wikidata for unindexed stadium
wd['latitude'] = coord.latitude
wd['longitude'] = coord.longitude
wd['valid'] = true
-- if we have a path of coordinates
if item:getBestStatements('P625')[2] then -- TODO make sure ordinal number
local i = 1
while item:getBestStatements('P625')[i] do
-- get coordinates
local statements = item:getBestStatements('P625')[i] --coordinate location
if statements ~= nil then -- check cordinates available
local coord = statements.mainsnak.datavalue.value
if type(coord.latitude) == 'number' and type(coord.longitude) == 'number' then
-- add coordinate data from wikidata for path
wd.path[i] = {}
wd.path[i].latitude = coord.latitude
wd.path[i].longitude = coord.longitude
wd.path[i].index = statements.qualifiers['P1545'][1]['datavalue']['value'] -- P1545 = series ordinal {a number]
local cycloneType = statements.qualifiers['P31'][1]['datavalue']['value']['id'] -- P31 = instance of [cyclone type]
if cycloneType then wd.path[i].cycloneType = mw.wikibase.getLabel( cycloneType ) end
end
end
i=i+1
end -- end while loop
end
end
end -- end if coordinate statements
--get image
statements = item:getBestStatements('P18')[1] --image
if statements ~= nil then
wd['image'] = 'File:' .. statements.mainsnak.datavalue.value
end
-- get occupants --TODO check for multi-occupancy
statements = item:getBestStatements('P466')[1] --occupant (i.e football club)
if statements ~= nil then
local clubID = statements.mainsnak.datavalue.value.id
if clubID then
local clubName = mw.wikibase.getLabel( clubID )
wd['description'] = '<small>Home ground of ' .. clubName .. '</small>'
end
end
-- get capacity
statements = item:getBestStatements('P1083')[1] --mcapacity
if statements ~= nil then
local capacity = tonumber(statements.mainsnak.datavalue.value.amount)
if capacity then
wd['description'] = wd['description'] .. '<small> (capacity: ' .. capacity .. ')</small>'
end
end
end
return wd
end
--------------------------------------------------------------------------------
p.getModuleData = function (frame, stadiumName)
local feature = {}
feature['name'] = ""
--feature['data'] = ""
feature['alias'] = ""
feature['description'] = ""
feature['image'] = ""
-- check the module stadium list for name match
-- set feature parameters from the module data
for _, params in pairs( stadiumDatabase.stadia ) do
if stadiumName == params[1] then -- if we have a match from the list
feature['name'] = params[1]
feature['latitude'] = params[2]
feature['longitude'] = params[3]
feature['alias'] = params[4]
feature['description'] = params[5]
feature['image'] = params[6]
break
end
end
return feature
end
-- function to construct JSON string for WHL in London map
p.getTestJSONstring = function(frame)
return '{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-0.066417, 51.60475] }, "properties": { "title": "White Hart Lane", "description": "Stadium of Tottenham Hotspur F.C.", "marker-symbol": "soccer", "marker-size": "large", "marker-color": "0050d0" } } '
end -- End the function.
-- function to construct JSON string
p.getJSONstring = function(frame)
local stadiumName = mw.getCurrentFrame():getParent().args['stadium1'] or "default name"
local str=stadiumName
local jsonString = '{ "type": "Feature", '
jsonString = jsonString .. ' "geometry": { "type": "Point", "coordinates": [-0.065833, 51.603333] }, '
jsonString = jsonString .. ' "properties": { "title": "[[White Hart Lane]]", '
jsonString = jsonString .. ' "description": "[[File:White Hart Lane Aerial.jpg|150px]]Tottenham Hotspur Football Club (1899-2017)", '
jsonString = jsonString .. ' "marker-symbol": "-number", "marker-size": "small", "marker-color": "dd50d0" } } '
--mapString = mapString .. '</mapframe>'
jsonString = '{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-0.066417, 51.60475] }, "properties": { "title": "[[Northumberland Development Project]]", "description": "[[File:NDProject2015.jpg|100px]]", "marker-symbol": "soccer", "marker-size": "large", "marker-color": "0050d0" } } '
jsonString = '{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-0.066417, 51.60475] }, "properties": { "title": "title", "description": "description", "marker-symbol": "soccer", "marker-size": "large", "marker-color": "0050d0" } } '
str = '<nowiki>' .. jsonString .. '</nowiki>'
--str = jsonString
return str
end -- End the function.
-- All modules end by returning the variable containing its functions to Wikipedia.
return p