Modul:Mapframe: Forskjell mellom sideversjoner

Fra Wikisida.no
Hopp til navigering Hopp til søk
(legger inn debugparameter (vis = debug) som vil gi debuginformasjon under kartet.)
(Zoomtabellen er tilpasset kartbredde i infobokser,)
Linje 196: Linje 196:
end
end


-- the distances need to be adjusted according to the size of the frame of the map

local function bboxzoom()
local function bboxzoom(factor)
for ix,v in ipairs(autozoom) do
for ix,v in ipairs(autozoom) do
if bbox.haversine and v.dist < bbox.haversine then
if bbox.haversine and v.dist*factor < bbox.haversine then
return v.zoom
return v.zoom
end
end
Linje 600: Linje 600:
centerLat = bbox.center.latitude
centerLat = bbox.center.latitude
centerLon = bbox.center.longitude
centerLon = bbox.center.longitude
zoom = bboxzoom()
local factor = width/300
zoom = bboxzoom(factor)
if visKoord == "debug" then
if visKoord == "debug" then
text = text .. " " .. dump(bbox) -- for debug
text = text .. " " .. dump(bbox) -- for debug

Sideversjonen fra 16. mai 2023 kl. 08:57

Denne modulen benyttes av kartmalen {{Kart}}. Se dokumentasjonssiden til denne malen for bruk og instruksjoner.


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 = {"Koordinater funnet"}

autozoom = {
	{ dist=2000,zoom=3}, 
	{ dist=700,zoom=4}, 
	{ dist=300,zoom=5}, 
	{ dist=150,zoom=6}, 
	{ dist=50,zoom=7}, 
	{ dist=30,zoom=8}, 
	{ dist=10,zoom=9}, 
}

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

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 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


local function addBbox(entity)
	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 .. " " .. 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
	return 

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

-- the distances need to be adjusted according to the size of the frame of the map
local function bboxzoom(factor)
	for ix,v in ipairs(autozoom) do
		if bbox.haversine  and v.dist*factor < 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)
	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)
	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)
		local line = geoline(id,stroke,title)
		table.insert(geojson, line)
		if wdlat and wdlon then
    		local point = mappoint(wdlat,wdlon,text,description,symbol)
			table.insert(geojson, point)
		end
	elseif meth == "geomask" then
		shape = geomask(id,stroke,opacity)
		table.insert(geojson, shape)
	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)
	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)
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 and numLegend < 99 then
				symbol = "1"
			end
			visEntity(geojson,id,stroke,opacity,method,symbol)
			count = count+1
			
		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)
	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
	visClaims(geojson,claims,stroke,opacity,"geoshape","bruk symbol")

	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 300
	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 entity = mw.wikibase.getEntity(id) or nil
	if not id 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 300
	end
	local height  = tonumber(args["height"]) or width
	if height == "full" then
		return ""
	end
	addBbox(entity)
	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 wdlat,wdlon,coordcat = wdCoords(entity)
	if not lat or not lon then
		lat = wdlat
		lon = wdlon
	end
	local centerLat = lat
	local centerLon = lon
    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 ""
	local align = args["align"]  or 'right'
	local lang = selectMapLanguage(entity) 
	
	local geojson = {}
	local stroke = "#ff0000"
	local title = nil
	local opacity = 0.1
	if includeProperty then
		opacity = 0.0
	end
	showEntity(geojson,id,entity,stroke,title,opacity)
	if args['marker'] and lat and lon then
    	local point = mappoint(lat,lon)
		table.insert(geojson, point)
	end
	includeLocation(geojson,entity)
	if includeProperty then
		local props = mw.text.split( includeProperty, ',', true )
		for i,pid in ipairs(props) do
			includeProp(geojson,entity,pid)
		end
	end
	local frameargs = {
    		['height'] = height,
    		['width'] = width,
    		['align'] = align,
    		['lang'] = lang
    	}
	if zoom and zoom == 'auto' then
  	
    	centerLat = bbox.center.latitude
    	centerLon = bbox.center.longitude
    	local factor = width/300
    	zoom = bboxzoom(factor)
    	if visKoord == "debug" then
    		text = text .. " " .. dump(bbox)   -- for debug
    	end
	end
    if zoom and zoom ~= 'auto' then
   	
    	frameargs['zoom'] = zoom
    	frameargs['latitude'] = centerLat
    	frameargs['longitude'] = centerLon
    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
	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 300
    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

return p