Module:Mapframe

From Random Island Wiki
Revision as of 23:26, 2 May 2018 by >Evad37 (allow makeCoords to return plain output)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Mapframe/doc

--Parameter for cleaned-up parent.args (whitespace trimmed, blanks removed)
local Args = {}

local defaults = {
	display = 'inline',
	text = 'Map',
	["frame-width"] = '300',
	["frame-height"] = '200'
}

function setCleanArgs(argsTable)
	local cleanArgs = {}
	for key, val in pairs(argsTable) do
		if type(val) == 'string' then
			val = val:match('^%s*(.-)%s*$')
			if val ~= '' then
				cleanArgs[key] = val
			end
		else
			cleanArgs[key] = val
		end
	end
	return cleanArgs
end

function isAffirmed(val)
	if not(val) then return false end
	local affirmedWords = ' add added affirm affirmed include included on true yes y '
	return string.find(affirmedWords, ' '..val..' ', 1, true ) and true or false
end

function isDeclined(val)
	if not(val) then return false end
	local declinedWords = ' decline declined exclude excluded false none not no n off omit omitted remove removed '
	return string.find(declinedWords , ' '..val..' ', 1, true ) and true or false
end

function makeContent(args)
	if args.raw then
		return args.raw
	end

	local content = {};
	local contentIndex = '';
	while args['type'..contentIndex] or args['from'..contentIndex] do
		local contentArgs = {}
		for k, v in pairs(args) do
			if string.match(k, '.*'..contentIndex) then
				contentArgs[string.gsub(k, contentIndex, '')] = v
			end
		end



		--contentArgs['type'] = Args['type'..contentIndex]
		--todo: Add other relevant args

		if contentIndex == '' then
			contentIndex = 1
		end

		content[contentIndex] = makeContentJson(contentArgs)

		contentIndex = contentIndex + 1
	end
	
	--Single item, no array needed
	if #content==1 then
		return content[1]
	end

	--Multiple items get placed in a FeatureCollection
	local contentArray = '[\n' .. table.concat( content, ',\n') .. '\n]'
	return contentArray
end

function parseCoords(coords)
	local parts = mw.text.split((mw.ustring.match(coords,'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')

	latParts = mw.text.split(parts[1], '°')
	longParts = mw.text.split(parts[2], '°')

	if latParts[2] == 'S' then latParts[1] = '-'..latParts[1] end
	if longParts[2] == 'W' then longParts[1] = '-'..longParts[1] end
	return tonumber(latParts[1]), tonumber(longParts[1])
end

function makeCoords(args, plainOutput) 
	local coords
	local frame = mw.getCurrentFrame()
	if args.coord then
		coords = frame:preprocess(args.coord)
	else
		coords = frame:preprocess('{{WikidataCoord|display=|'..(args.id or args.ids or mw.wikibase.getEntityIdForCurrentPage())..'}}')
	end
	local lat, long = parseCoords(coords)
	if plainOutput then
		return lat, long
	end
	return {[0] = long, [1] = lat}
end

function makeContentJson(args)
	local data = {}

	if args.type == 'point' then
		data.type = "Feature"
		data.geometry = {
			type = "Point",
			coordinates = makeCoords(args)
		}
		data.properties = {
			title = args.title or mw.getCurrentFrame():getParent():getTitle(),
			["marker-symbol"] = args.marker or "marker",
			["marker-color"] = "5E74F3"
		}
	else
		data.type = "ExternalData"

		if args.type == "data" or args.from then
			data.service = "page"
		elseif args.type == "line" then
			data.service = "geoline"
		elseif args.type == "shape" then
			data.service = "geoshape"
		elseif args.type == "shape-inverse" then
			data.service = "geomask"
		end

		if args.id or args.ids or (not (args.from) and mw.wikibase.getEntityIdForCurrentPage()) then
			data.ids = args.id or args.ids or mw.wikibase.getEntityIdForCurrentPage()
		else 
			data.title = args.from
		end

		data.properties = {
			stroke = args["stroke-color"] or args["stroke-colour"] or "#ff0000",
			["stroke-width"] = tonumber(args["stroke-width"]) or 6
		}

	end

	data.properties.title = args.title or mw.getCurrentFrame():getParent():getTitle()
	if args.description then
		data.properties.description = args.description
	end

	return mw.text.jsonEncode(data)
end

function makeTagAttribs(args, isTitle)
	local attribs = {}
	if args.zoom then
		attribs.zoom = args.zoom
	end
	if isDeclined(args.icon) then
		attribs.class = "no-icon"
	end
	if args.type == 'point' then
		local lat, long = makeCoords(args, 'plainOutput')
		attribs.latitude = tostring(lat)
		attribs.longitude = tostring(long)
	end
	if isAffirmed(args.frame) and not(isTitle) then
		attribs.width = args["frame-width"] or defaults["frame-width"]
		attribs.height = args["frame-height"] or defaults["frame-height"]
		if args["frame-lat"] or args["frame-latitude"] then
			attribs.latitude = args["frame-lat"] or args["frame-latitude"]
		end
		if args["frame-long"] or args["frame-longitude"] then
			attribs.longitude = args["frame-long"] or args["frame-longitude"]
		end
		if isAffirmed(args.plain) then
			attribs.frameless = "1"
		else
			attribs.text = args.text or defaults.text
		end
	else
		attribs.text = args.text or defaults.text
	end
	return attribs
end

function makeTitleOutput(args, tagContent)
 	local titleTag = mw.text.tag('maplink', makeTagAttribs(args, true), tagContent)
	local spanAttribs = {
		style = "font-size: small;",
		id = "coordinates"
	}
	return mw.text.tag('span', spanAttribs, titleTag)
end

function makeInlineOutput(args, tagContent)
	local tagName = 'maplink'
	if args.frame then
		tagName = 'mapframe'
	end

	return mw.text.tag(tagName, makeTagAttribs(args), tagContent)
end

local p = {}

function p.main(frame)
	local parent = frame.getParent(frame)
	Args = setCleanArgs(parent.args)
 
	local tagContent = makeContent(Args)

	local display = mw.text.split(Args.display or defaults.display, '%s*,%s*')
	local displayInTitle = display[1] == 'title' or display[2] == 'title'
	local displayInline = display[1] == 'inline' or display[2] == 'inline'

	local output
	if displayInTitle and displayInline then
		output = makeTitleOutput(Args, tagContent) .. makeInlineOutput(Args, tagContent)
	elseif displayInTitle then
		output = makeTitleOutput(Args, tagContent)
	elseif displayInline then
		output = makeInlineOutput(Args, tagContent)
	else
		error( 'Invalid display parameter')
	end

	-- return output -- temporary for testing, to see the JSON being produce

	return frame:preprocess(output)
end

return p