Jump to content

Module:Cyclone map

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Jts1882 (talk | contribs) at 10:50, 22 May 2020 (Undid revision 958186609 by Jts1882 (talk) no it can't). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

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 = featureCollection .. sep ..
		 '{"type": "FeatureCollection", "features": [' 
	     ..'{"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