Modul:Sandkasse/Haros/kart

Fra Wikipedia, den frie encyklopedi
Moduldokumentasjon


local math_mod = require( "Module:Math" )

local useLanguage = {
	["Q17"] = "en",     -- Japan
	["Q79"] = "en",     -- Egypt
	["Q115"] = "en",    -- Etiopia
	["Q148"] = "en",    -- Kina
	["Q230"] = "en",    -- Georgia
	["Q399"] = "en",    -- Armenia
	["Q423"] = "en",    -- Nord-Korea
	["Q424"] = "en",    -- Kambodsja
	["Q668"] = "en",    -- India
	["Q711"] = "en",    -- Mongolia
	["Q794"] = "en",    -- Iran
	["Q796"] = "en",    -- Irak
	["Q801"] = "en",    -- Israel
	["Q810"] = "en",    -- Jordan
	["Q819"] = "en",    -- Laos
	["Q822"] = "en",    -- Libanon
	["Q836"] = "en",    -- Myanmar
	["Q842"] = "en",    -- Oman
	["Q843"] = "en",    -- Pakistan
	["Q846"] = "en",    -- Qatar
	["Q851"] = "en",    -- Saudi-Arabia
	["Q858"] = "en",    -- Syria
	["Q865"] = "en",    -- Republikken Kina - Taiwan
	["Q869"] = "en",    -- Thailand
	["Q878"] = "en",    -- De forente arabiske emirater
	["Q884"] = "en",    -- Sør-Korea
	["Q889"] = "en",    -- Afghanistan
	["Q902"] = "en",    -- Bangladesh
	["Q917"] = "en",    -- Bhutan
	["Q986"] = "en",    -- Eritrea
	["Q23792"] = "en"     -- Palestina
}


local p = {}

local legend = {}
local numLegend = 0
local maxLegend = 5

local bbox = {}
bbox.min = {}
bbox.max = {}
bbox.center = {}
bbox.dist = {}
bbox.txt = {"dette er en test"}

local function hasLocalCoord()
	local pageWikitext = mw.title.getCurrentTitle():getContent()
	local treff = pageWikitext:match("{{[Kk]oord|[^}]+}}")
	if treff then
		local treffVis = treff:match("vis")
		local treffTittel = treff:match("tittel")
		local treffTekst = treff:match("tekst")
		if treff and treffVis and treffTittel then 
			return treff
		end
	end
	return nil
end

function enkartlenke(treff)
	local treff1 = mw.text.split( treff, "}", true )
	args = mw.text.split( treff1[1], "|", true )
	return args
end
function kartlenker(geojson)
	local pageWikitext = mw.title.getCurrentTitle():getContent()
	for treff in pageWikitext:gmatch("{{kartlenke|[^}]+}}") do
		geojson = enkartlenke(geojson,treff)
	end
	return geojson
end

local function lonlat(args)
	local newargs = args
	if not args["lat"] and not args["lon"] then
		if args["breddegrad"] and args["lengdegrad"] then
			newargs["lat"] = tonumber(args["breddegrad"]) or nil
			newargs["lon"] = tonumber(args["lengdegrad"]) or nil
		end
	end
	return newargs
end

local function selectSingleClaim(claims)
	if not claims then
		return nil
	end
	local selectedClaim = nil
	for idx,claim in pairs(claims) do
		if claim.rank == 'preferred' then
			return claim
		end
		if claim.rank == 'normal' then
			if not selectedClaim then
				selectedClaim = claim
			end
		end
	end
	return selectedClaim
end

local function osmLink(entity)
	local osm = ""
	local osmval = entity:getBestStatements("P402")
	if osmval and osmval[1] and osmval[1].mainsnak["datavalue"] then
		local osmid = osmval[1].mainsnak["datavalue"].value or ""
		if osmid and osmid ~= "" then
			--osm = "<br/> vis på [https://www.openstreetmap.org/relation/" .. osmid .. " OSM]"
			osm = "[[Kategori:Sider hvor Wikidata har lenker til OpenStreetMap relation]]"
		end
	end
	return osm
end

local function allOkSnaks(entity,datatype)
	local retur = {}
	if entity.claims then
		for propid,props in pairs(entity.claims) do
			for idx,claim in ipairs(props) do
				if claim.rank == 'preferred' or claim.rank == 'normal' then
					local snak = claim.mainsnak
					if snak.snaktype == "value" and snak.datatype == datatype then
						--	local txt = propid .. " " .. idx .. " " .. claim.rank .. " " 
						--	.. snak.snaktype
						--	table.insert(retur,txt)
						local sn = {}
						sn.snak = snak
						sn.qu = claim.qualifiers
						table.insert(retur,sn)
					end
				end
			end
		end
	end
	return retur
end

function dump(item)
	return "<pre>" .. mw.text.jsonEncode(item, mw.text.JSON_PRETTY) .. "</pre>"
end

-- From en:Module:Sandbox/Hellknowz/CoordDistance
function HaversineDistance(lat1,lon1,lat2,lon2)

    local radius = 6371 -- km

    local dlat = math.rad(lat2-lat1)
    local dlon = math.rad(lon2-lon1)
    local a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    local c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    return radius * c

end

local function addBbox(entity)
	local retur = bbox.txt
	for idx,sn in ipairs(allOkSnaks(entity,"globe-coordinate")) do
		local snak = sn.snak
		local val = snak.datavalue.value
		local qu = sn.qu
		local qutxt = ""
		if qu and qu.P518 then
			qutxt = " P518 " .. mw.wikibase.formatValues(qu.P518)
		end
		if not bbox.min.longitude or bbox.min.longitude > val.longitude then
			bbox.min.longitude = val.longitude
		end
		if not bbox.min.latitude or bbox.min.latitude > val.latitude then
			bbox.min.latitude = val.latitude
		end
		if not bbox.max.longitude or bbox.max.longitude < val.longitude then
			bbox.max.longitude = val.longitude
		end
		if not bbox.max.latitude or bbox.max.latitude < val.latitude then
			bbox.max.latitude = val.latitude
		end
		local txt = snak.property .. " " .. entity.id .. " "
		.. mw.wikibase.label(entity.id) .. " ".. qutxt .. " " .. val.longitude .. " " .. val.latitude
		table.insert(bbox.txt,txt)
	end
	if (bbox.min.longitude) then
	bbox.center.longitude = (bbox.min.longitude + bbox.max.longitude)/2
	bbox.center.latitude = (bbox.min.latitude + bbox.max.latitude)/2
	bbox.dist.longitude = (bbox.max.longitude - bbox.min.longitude)
	bbox.dist.latitude = (bbox.max.latitude - bbox.min.latitude)
	bbox.haversine = HaversineDistance(
		bbox.max.latitude,bbox.max.longitude,
		bbox.min.latitude,bbox.min.longitude)
    end
	--bbox.txt = retur
	return --bbox

end
local function bboxlist(ids)
	for ix,id in ipairs(ids) do
		local entity = mw.wikibase.getEntity(id)
		addBbox(entity)
	end
end

-- From en:Module:Sandbox/Hellknowz/CoordDistance
function HaversineDistance(lat1,lon1,lat2,lon2)

    local radius = 6371 -- km

    local dlat = math.rad(lat2-lat1)
    local dlon = math.rad(lon2-lon1)
    local a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    local c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    return radius * c

end


local function bboxzoom()
	for ix,v in ipairs(autozoom) do
		if bbox.haversine  and v.dist < bbox.haversine then
			return v.zoom
		end
	end
	return 12
end

local function wdCoords(entity)
	local wdlat,wdlon = nil,nil
	local category = ""
	if entity.claims and entity.claims["P625"] then
		local claim = selectSingleClaim(entity.claims["P625"])
		if claim and claim.mainsnak.datavalue then
			local coord = claim.mainsnak.datavalue.value or nil
			wdlat = coord["latitude"] or nil
			wdlon = coord["longitude"] or nil
		end
	end
	if not wdlat then
		if entity.claims and entity.claims["P159"] then
			local claim = selectSingleClaim(entity.claims["P159"])
			if claim and claim.qualifiers then
				local qual = claim.qualifiers["P625"]
				if qual and  qual[1] and qual[1].datavalue then
					local coord = qual[1].datavalue.value or nil
					wdlat = coord["latitude"] or nil
					wdlon = coord["longitude"] or nil
				end
				if wdlat then
					category = "[[Kategori:Artikler med koordinater hentet fra P159]]"
				end
			end
		end
	end
	return wdlat,wdlon,category
end


local function wdGeoShape(entity)
	local shape = nil
	if entity.claims and entity.claims["P3896"] then
		local claim = selectSingleClaim(entity.claims["P3896"])
		local value = claim.mainsnak.datavalue.value or nil
		if value then
			local prop = {}
			local shape = {
	       	["type"] = "ExternalData",
			["service"] = "page",
			["title"] =  value,
			["properties"] = prop
		}
		return shape
		end
	end
	return nil
end

local function selectMapLanguage(entity)
	local lang = "local"
	if entity.claims and entity.claims["P17"] then
		local claim = selectSingleClaim(entity.claims["P17"])
		if claim and claim.mainsnak.datavalue then
		    local value = claim.mainsnak.datavalue.value or nil
		    if value then
			    lang = useLanguage[value["id"]] or "local"
			end
		end
	end
	return lang
end

local function mappoint(latitude,longitude,title,description,symbol,letter)
	local point = {}
    point["type"] = "Feature"
    local geo = {}
    geo["type"] = "Point"
	local pos = {}
    geo["coordinates"] = pos
    -- ensure the coordinates are not too long for the geojson
    geo["coordinates"][1] = math_mod._round( longitude, 6 )
    geo["coordinates"][2] = math_mod._round( latitude, 6 )
    local prop = {}
    if title then
    	prop["title"] = title
    end
    if description then
    	prop["description"] = description
    end
    if symbol and title then
		numLegend = numLegend + 1
		prop["marker-symbol"] = tostring(numLegend)
		table.insert(legend,title)
    end
    prop["marker-size"] = "small"
    point["properties"] = prop
    point["geometry"] = geo
    return point
end

local function geoline(id,stroke,title)
	local line = {
		["type"] = "ExternalData",
		["service"] = "geoline",
		["ids"] =  id,
        ['properties'] = {
        	["stroke"] = stroke,
        	["stroke-width"] = 2,
        	["marker-size"] = "small",
        	--["marker-symbol"] = "-letter",
        	["title"] = title
        }
     }
     return line
end

local function geoshape(id,stroke,opacity)
	local line = {
		["type"] = "ExternalData",
		["service"] = "geoshape",
		["ids"] =  id,
        ['properties'] = {
        	["stroke"] = stroke,
        	["stroke-width"] = 2,
        	["fill"] = "#ff0000",
        	["fill-opacity"] = opacity or 0.1
        }
     }
     return line
end

local function geomask(id,stroke,opacity)
	local line = {
		["type"] = "ExternalData",
		["service"] = "geomask",
		["ids"] =  id,
        ['properties'] = {
        	["stroke"] = stroke,
        	["stroke-width"] = 1,
        	["fill-opacity"] = opacity or 0.05
        }
     }
     return line
end


local function showEntity(geojson,id,entity,stroke,title,opacity,createLink,method,symbol,letter)
	local wdlat,wdlon = wdCoords(entity)
	local text = title  or mw.wikibase.label(id)
	local link = mw.wikibase.sitelink(id)
	local meth = method or "geoshape"
	if createLink and text and link then
		text = "[[" .. link .. "|" .. text .. "]]"
	end
	local description = nil
	if createLink then
		if entity.claims and entity.claims["P18"] then
			local claim = selectSingleClaim(entity.claims["P18"])
			if claim then
	   			local image = claim.mainsnak.datavalue.value or nil
				if image then
					description = "[[File:" .. image .. "]]"
				end
			end
		end
	end
	local shape = nil
	if meth == "geoshape" then
		shape = geoshape(id,stroke,opacity)
		table.insert(geojson, shape)
		if wdlat and wdlon then
    		local point = mappoint(wdlat,wdlon,text,description,symbol,letter)
			table.insert(geojson, point)
		end
	elseif meth == "geomask" then
		shape = geomask(id,stroke,opacity)
		table.insert(geojson, shape)
	elseif meth == "geoxxx" then
		shape = geoshape(id,stroke,opacity)
		table.insert(geojson, shape)
		local line = geoline(id,stroke,title)
		table.insert(geojson, line)
		if wdlat and wdlon then
    		local point = mappoint(wdlat,wdlon,text,description,symbol,letter)
			table.insert(geojson, point)
		end
	end
    return geojson
end

-- Visualize an entity through boundary, marker with name of entity. geojson updated by showEntity
local function visEntity(geojson,id,stroke,opacity,method,symbol,letter)
	if not id then
		return nil
	end
	local entity = mw.wikibase.getEntity(id)
	if not entity then
		return nil
	end
	return showEntity(geojson,id,entity,stroke,nil,opacity,"createLink",method,symbol,letter)
end

local function getcurrentqids(claims)
	local qids = {}
	for idx,claim in pairs(claims) do
		local qualifiers = claim.qualifiers
		local endtime = nil
		if qualifiers then
			endtime = claim.qualifiers["P582"] or nil
		end
		-- todo: Test on end time simplified. This assumes that all end times are in the past
		if not endtime then
			if claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value then
				local qid = claim.mainsnak.datavalue.value['id'] or nil
				if qid then
					table.insert(qids, qid)
				end
			end
		end
	end
	return qids
end

local function visClaims(geojson,claims,stroke,opacity,method,usesymbol)
	local count = 0
	for idx,claim in pairs(claims) do
		local qualifiers = claim.qualifiers
		local endtime = nil
		if qualifiers then
			endtime = claim.qualifiers["P582"] or nil
		end
		-- todo: Test on end time simplified. This assumes that all end times are in the past
		if not endtime then
			local id = claim.mainsnak.datavalue.value['id'] or nil
			local symbol = nil
			if usesymbol then
				if useymbol == "1" and numLegend < 100 then
					symbol = "1"
				end
				if not usesymbol =="1" then
					symbol = usesymbol
				end
			end
			visEntity(geojson,id,stroke,opacity,method,symbol)
		end
	end

	return geojson
end

local function includeLocation(geojson,entity)
	if not entity or not entity.claims then
		return geojson
	end	

	local claims = entity.claims["P131"] or entity.claims["P276"] or nil
	if not claims then
		return geojson
	end	
	qids = getcurrentqids(claims)
	bboxlist(qids)
	local stroke = "#888888"
	local opacity = 0.1
	local shape = geomask(qids,stroke,opacity)
	table.insert(geojson, shape)
	return geojson
end

local function includeProp(geojson,entity,prop,symbol)
	if not entity or not prop or not entity.claims then
		return nil
	end
	local claims = entity.claims[prop] or nil
	if not claims then
		return nil
	end
	
	local stroke = "#880000"
	local opacity = 0.1
	local useprop = "1"
	if symbol then
		useprop = symbol
	end
	visClaims(geojson,claims,stroke,opacity,"geoshape",useprop)

	return geojson
end

local function makeLegend()
	local text = ""
	for idx,leg in ipairs(legend) do
		text = text  .. idx .. " = " .. leg .. "<br/>"
	end
	if numLegend>maxLegend then
    	text = string.format([[
				<div class="mw-collapsible mw-collapsed">
					<div class="sentrert">%s nummererte markører</div>
					<div class="mw-collapsible-content">%s</div>
				</div>
			]], numLegend, text)
	end
	if numLegend > 0 then
		text = text .. "[[Kategori:Artikler med kart med nummererte markører]]"
	end
	return text
end

local function mapNoWikidata(args,infoboks)
	local geojson = {}
	local zoom = args["zoom"] or 8
	local lat = args["latitude"] or args["lat"]
	local lon = args["longitude"] or args["lon"]
	if not lat or not lon then
		return ""
	end
    local point = mappoint(lat,lon)
	local width = tonumber(args["width"]) or 290
	local height  = tonumber(args["height"]) or width
	local text = args["text"]  or ""
	table.insert(geojson, point)
	local frameargs = {
    		['height'] = height,
    		['width'] = width,
    		['align'] = align
    		}
    if zoom and zoom ~= 'auto' then
    	frameargs['zoom'] = zoom
    	frameargs['latitude'] = lat
    	frameargs['longitude'] = lon
    end
	local linkargs = {
    		['height'] = height,
    		['width'] = width,
    		['align'] = align,
    		['latitude'] = lat,
    		['longitude'] = lon,
    		['zoom'] = zoom or 8
    	}
    local mlink = ''
    local klink = ''
    if infobox then
--    	mlink = '<br/>' .. mw.getCurrentFrame():extensionTag('maplink', mw.text.jsonEncode(geojson), linkargs)
    	klink = '<br/>' .. mw.getCurrentFrame():expandTemplate{title='koord',args = { lat, lon, vis='tekst'} }
    end
   	frameargs['text'] = text .. mlink .. klink
	return  mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), frameargs)

end

local function map2(args,infobox,visKoord)
	local id = args['id'] or args[1] or nil
	if id == "" then
		id = nil
	end
	local p131 = args['p131']
	local entity = mw.wikibase.getEntity(id) or mw.wikibase.getEntity(p131) or nil
	if not id and not p131 then
		if not entity then
			return ""
		end
		id = entity.id
	end
	local width = args["width"] or nil
	if width ~= "full" then
		width = tonumber(args["width"]) or 290
	end
	local height  = tonumber(args["height"]) or width
	if height == "full" then
		return ""
	end
	local zoom = args["zoom"] or 8
	local lat = args["latitude"] or args["lat"]
	local lon = args["longitude"] or args["lon"]
	local includeProperty = args["vis egenskap"]  or args["include property"] or nil
	local includeElement = args["vis element"]  or args["include element"] or nil
	local wdlat,wdlon,coordcat = wdCoords(entity)
	if not lat or not lon then
		lat = wdlat
		lon = wdlon
    end
    if not lon or not lat then
    	return "[[Kategori:Artikler hvor kartmodul mangler koordinater]]"
    end
	local text = args["text"]  or mw.wikibase.label(id) or mw.wikibase.label(p131) or ""
	local align = args["align"]  or 'right'
	local lang = selectMapLanguage(entity) 
	local json = args["json"]  or nil
	local geojson = {}
	local stroke = "#ff0000"
	local title = nil
	local opacity = 0.1
	--if includeProperty then
	--end
	if id then
		showEntity(geojson,id,entity,stroke,title,opacity)
	end
	if p131 then
		local stroke = "#888888"
		local opacity = 0.1
		local shape = geomask(p131,stroke,opacity)
		table.insert(geojson, shape)
	end
	if args['marker'] and lat and lon then
    	local point = mappoint(lat,lon)
		table.insert(geojson, point)
	end
	includeLocation(geojson,entity)
	opacity = 0.0
	if includeElement then
		local els = mw.text.split( includeElement, ',', true )
		for i,id in ipairs(els) do
			visEntity(geojson,mw.text.trim(id),stroke,opacity,"geoshape",1)
		end
	end
	if includeProperty then
		local props = mw.text.split( includeProperty, ',', true )
		for i,pid in ipairs(props) do
			includeProp(geojson,entity,mw.text.trim(pid))
		end
	end
	local frameargs = {
    		['height'] = height,
    		['width'] = width,
    		['align'] = align,
    		['lang'] = lang
    	}
    if zoom and zoom ~= 'auto' then
    	frameargs['zoom'] = zoom
    	frameargs['latitude'] = lat
    	frameargs['longitude'] = lon
    end
	if zoom and zoom == 'auto' then
		zoom = nil
	end
	local linkargs = {
    		['height'] = height,
    		['width'] = width,
    		['align'] = align,
    		['latitude'] = lat,
    		['longitude'] = lon,
    		['zoom'] = zoom or 8,
    		['lang'] = lang
    	}
    	local mlink = ''
        local klink = ''
     	if text then
    		if infobox then
    			local koordmal = "[[Kategori:Artikler med lokal koord mal til tittelfelt]]"
    			if not hasLocalCoord() then
    				visKoord = "tittel"
    				koordmal = ""
    			end
--    			mlink = '<br/>' .. mw.getCurrentFrame():extensionTag('maplink', mw.text.jsonEncode(geojson), linkargs)
                if visKoord and visKoord == 'tittel' then
    	            klink = '<br/>' .. mw.getCurrentFrame():expandTemplate{title='koord',args = { lat, lon , vis='tekst,tittel'} }
    	        else
    	            klink = '<br/>' .. mw.getCurrentFrame():expandTemplate{title='koord',args = { lat, lon , vis='tekst'} }
    	        end
    			frameargs['frameless'] = 'frameless'
    			return  mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), frameargs) .. '<br/>'
    			.. makeLegend() .. text .. mlink .. klink
    			.. osmLink(entity) .. koordmal .. coordcat
    		end
    		frameargs['text'] = makeLegend() .. text .. mlink .. klink
     	end
	if json then
		return "<pre>" .. mw.text.jsonEncode(args, mw.text.JSON_PRETTY) .. "</pre>" 
		.. "<pre>" .. mw.text.jsonEncode(geojson, mw.text.JSON_PRETTY) .. "</pre>" 
	end
	return  mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), frameargs)
	
--    return "<pre>" .. mw.text.jsonEncode(geojson, mw.text.JSON_PRETTY) .. "</pre>" 
end

function p.infoboxLocation(frame)
--	return "<pre>" .. mw.text.jsonEncode(frame:getParent().args, mw.text.JSON_PRETTY) .. "</pre>"
    local args = lonlat(mw.getCurrentFrame():getParent().args)
    local argframe = mw.getCurrentFrame().args
    local vis = args["vis"] or argframe["vis"] or ""
    if vis == "nei" then
    	return ""
    end
    args["width"] = args["width"] or argframe["width"] or 290
    local zoom = args["zoom"] or argframe["zoom"] or nil
    if zoom and zoom ~= 'auto' then
	    args["zoom"] = tonumber(args["zoom"]) or tonumber(argframe["zoom"]) or 8
	else 
		if not zoom then
			args["zoom"] = 8
		else
			args["zoom"] = 'auto'
		end
    end
	if not args["vis egenskap"] then
		args["vis egenskap"] = argframe["vis egenskap"] or nil
	end
    args[1] = ""
    args["align"] = "center"
	local maptxt = map2(args,1,vis)
	if not maptxt or maptxt =="" then
		return mapNoWikidata(args,1)
	end
	return maptxt
end

function p.map(frame)
--	return "<pre>" .. mw.text.jsonEncode(frame:getParent().args, mw.text.JSON_PRETTY) .. "</pre>"
	return map2(mw.getCurrentFrame():getParent().args)
end

function p.printEntity(frame)
	local id = "Q1538332" --frame.args[1]
	local entity = mw.wikibase.getEntity(id)
	--if entity then return "<pre>" .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. "</pre>" end
end

function p.test(frame)
	local id = "Q1314643" --frame.args[1]
	local entity = mw.wikibase.getEntity(id)
	addBbox(entity)
	local txt = bbox
	local geojson = {}
	includeLocation(geojson,entity)

	--enkartlenke("{{kartlenke|qid=123|bokstav=A}}")
	--if txt then 
	return "<pre>" .. mw.text.jsonEncode(bbox, mw.text.JSON_PRETTY) .. "</pre>" --end 
end

return p