Module:Infobox dim
![]() | This module is rated as beta, and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected. |
![]() | This Lua module is used on many pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
Code to convert information about physical size of objects into parameters for geohack or mapframes.
The following parameters describe the physical object size:
- length_km
- length_mi
- width_km
- width_mi
- area_km2
- area_mi2
- area_ha
- area_acre
When producing scale or zoom, module can use size of object on screen specified via |viewport_cm=
or {{{viewport_px}}}
. Defaults to 10cm, to match geohack.
In addition, the module can accept and convert between geohack parameters:
- dim
- scale
- type (e.g., "mountain" or "city")
- population (for type="city" only)
Usage
{{#invoke:Infobox dim|dim}}
Generates dim string (e.g, "24km") suitable for geohack link
{{#invoke:Infobox dim|scale}}
Generates scale (e.g, 100000) suitable for geohack link
{{#invoke:Infobox dim|zoom}}
Generates Open Street Map zoom level (e.g, 12) suitable for use in mapframes
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, args)
dim = tonumber(dim)
args = args or {}
if not dim or dim <= 0 then return end
local units = args.units
if units and string.lower(units) == 'km' then
dim = dim*1000
end
-- geohack assumes a map width of 10cm on the screen, so use this as default
local viewport = args.viewport_cm and args.viewport_cm / 100
or args.viewport_px and args.viewport_px / ppm or 0.1
return dim / viewport
end
-- inverse of above function, returning dim in km
local function geohackScaleToDim(scale, args)
scale = tonumber(scale)
args = args or {}
if not scale or scale <= 0 then return end
local viewport = args.viewport_cm and args.viewport_cm / 100
or args.viewport_px and args.viewport_px / ppm or 0.1
return scale * viewport * 1e-3
end
-- Convert from Geohack's types to Geohack's scale levels
local function geohackTypeToScale(args)
args = args or {}
local type = args.type
local typeScale = mw.loadData('Module:Infobox_dim/data')
local scale
if typeScale[type] then
scale = typeScale[type]
end
local population = tonumber(args.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, args)
-- 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)
dim = geohackScaleToDim(scale, args)
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*)")
args.units = units
scale = geohackDimToScale(dim, args)
end
if not scale then
dim = convertDim(args)
args.units = 'km'
scale = dim and geohackDimToScale(dim, args)
end
if args.type and not scale then
args.units = ''
scale = geohackTypeToScale(args)
end
if not scale then return end
scale = math.floor(scale+0.5)
-- keep scale within sane bounds
if scale < 2000 then
scale = 2000
end
if scale > 250e6 then
scale = 250e6
end
return scale
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