Jump to content

Module:Infobox dim

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Hike395 (talk | contribs) at 12:53, 4 October 2024 (wrong function). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

require('strict')
local getArgs = require('Module:Arguments').getArgs
local p = {}

local log2 = 0.693147181
local ppm = 1000/0.3  -- pixels per meter, from 0.3 mm / pixel

-- Convert from Geohack's scale and dim levels to OSM style zoom levels as used by <maplink>
local function geohackScaleToMapZoom(scale)
	scale = tonumber(scale)
	if not scale or scale <= 0 then return end
	-- Approximation of https://wiki.openstreetmap.org/wiki/Zoom_levels
    -- pixel/m from OSM is exact, so use zoom level 9 and 0.3 mm/pixel
	return math.log(305.748*ppm/scale)/log2 + 9
end

local function geohackDimToScale(dim, units)
	dim = tonumber(dim)
	if not dim or dim <= 0 then return end
	if units and string.lower(units) == 'km' then
		dim = dim*1000
	end
	-- Using 0.3 mm/pixel from https://wiki.openstreetmap.org/wiki/Zoom_levels, and
	-- assuming dim spans a map width of 800px (= 24cm on screen)
	return ppm/800 * dim
end

-- inverse of above function, returning dim in km
local function geohackScaleToDim(scale)
	scale = tonumber(scale)
	if not scale or scale <= 0 then return end
	return 0.8/ppm * scale
end

-- Convert from Geohack's types to Geohack's scale levels
local function geohackTypeToScale(type, population)
	local typeScale = {
		adm1st = 1000000,
		adm2nd = 300000,
		adm3rd = 100000,
		airport = 30000,
		city = 100000,
		country = 10000000,
		edu = 10000,
		event = 50000,
		forest = 50000,
		glacier = 50000,
		isle = 100000,
		landmark = 10000,
		mountain = 100000,
		pass = 10000,
		railwaystation = 10000,
		river = 100000,
		satellite = 10000000,
		waterbody = 100000,
		camera = 10000
	}
	local scale
	if typeScale[type] then
		scale = typeScale[type]
	end
	population = tonumber(population)
	if type == 'city' and population and population > 0 then
		-- assume city is a circle with density of 1000/square kilometer
		-- compute diameter, in meters
		local diam = 35.68*math.sqrt(population)
		-- convert to scale
		scale = geohackDimToScale(diam)
		-- don't zoom in too far
		if scale < 25000 then
			scale = 25000
		end
	end
	return scale
end

-- Convert from dimension of object to Geohack dim level
local function computeDim(length,width,area)
	if length and width then
		return 0.5*math.max(length,width)
	end
	if length then return 0.5*length end
	if width then return 0.5*width end
	if area then return 0.977*math.sqrt(area) end
end

local function convertDim(args)
	for _, arg in pairs({'length_mi','length_km','width_mi','width_km',
		'area_mi2','area_km2','area_acre','area_ha'}) do
		args[arg] = args[arg] and mw.ustring.gsub(args[arg],",","")
		args[arg] = tonumber(args[arg])
		if args[arg] and args[arg] <= 0 then args[arg] = nil end
	end
	local length = args.length_mi and 1.60934*args.length_mi or args.length_km
	local width = args.width_mi and 1.60934*args.width_mi or args.width_km
	local area = args.area_acre and 0.00404686*args.area_acre or 
		args.area_ha and 0.01*args.area_ha or 
		args.area_mi2 and 2.58999*args.area_mi2 or args.area_km2
	local dim = computeDim(length, width, area)
	return dim
end

-- Module entry points
function p._dim(args)
	if args.dim then return args.dim end
	local dim = convertDim(args)
	if not dim then
		local scale = args.scale or geohackTypeToScale(args.type, args.population)
		dim = geohackScaleToDim(scale)
	end
	return dim and tostring(math.floor(dim+0.5))..'km'
end

function p._scale(args)
	if args.scale then return args.scale end
	local dim, units, scale
	if args.dim then
		dim, units = mw.ustring.match(args.dim,"^([-%d%.]+)%s*(%D*)")
		scale = geohackDimToScale(dim, units)
	end
	if not scale then
		dim = convertDim(args)
		scale = dim and geohackDimToScale(dim, 'km')
	end
	if args.type and not scale then
		scale = geohackTypeToScale(args.type, args.population)
	end
	return scale and math.floor(scale+0.5)
end

function p._zoom(args)
	local scale = p._scale(args)
	if scale then
		local zoom = geohackScaleToMapZoom(scale)
		return zoom and math.floor(zoom+0.5)
	end
end		

-- Template entry points
function p.dim(frame)
	return p._dim(getArgs(frame)) or ''
end

function p.scale(frame)
	return p._scale(getArgs(frame)) or ''
end

function p.zoom(frame)
	return p._zoom(getArgs(frame)) or ''
end

return p