<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AInfobox_dim</id>
	<title>Module:Infobox dim - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AInfobox_dim"/>
	<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Infobox_dim&amp;action=history"/>
	<updated>2026-04-04T22:32:26Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://the-democratika.com/wiki/index.php?title=Module:Infobox_dim&amp;diff=6787&amp;oldid=prev</id>
		<title>&gt;Hike395: bail early from geohackTypeToDim</title>
		<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Infobox_dim&amp;diff=6787&amp;oldid=prev"/>
		<updated>2025-01-07T06:07:16Z</updated>

		<summary type="html">&lt;p&gt;bail early from geohackTypeToDim&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;require(&amp;#039;strict&amp;#039;)&lt;br /&gt;
local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local log2 = 0.693147181&lt;br /&gt;
local ppm = 1000/0.3  -- pixels per meter, from 0.3 mm / pixel from https://wiki.openstreetmap.org/wiki/Zoom_levels&lt;br /&gt;
&lt;br /&gt;
-- To convert to OSM zoom level, we need to know meters per pixel at zoom level 9&lt;br /&gt;
-- On the equator, it&amp;#039;s 305.748 meters/pixel according to https://wiki.openstreetmap.org/wiki/Zoom_levels&lt;br /&gt;
-- This quantity depends on the latitude (which we don&amp;#039;t have easy access to)&lt;br /&gt;
-- Instead, we&amp;#039;ll be correct at 30N, cos(30 degrees) = sqrt(3)/2&lt;br /&gt;
local metersPerPixelLevel9 = 305.748*math.sqrt(3)/2&lt;br /&gt;
&lt;br /&gt;
-- Convert from Geohack&amp;#039;s scale to OSM style zoom levels as used by &amp;lt;maplink&amp;gt;&lt;br /&gt;
local function geohackScaleToMapZoom(scale)&lt;br /&gt;
	scale = tonumber(scale)&lt;br /&gt;
	if not scale or scale &amp;lt;= 0 then return end&lt;br /&gt;
	return math.log(metersPerPixelLevel9*ppm/scale)/log2 + 9&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- compute the viewport size (on screen) in meters, assuming ppm pixels per  meter on screen&lt;br /&gt;
local function computeViewport(args)&lt;br /&gt;
	local viewport_cm = tonumber(args.viewport_cm)&lt;br /&gt;
	local viewport_px = tonumber(args.viewport_px)&lt;br /&gt;
	return viewport_cm and viewport_cm / 100 or viewport_px and viewport_px / ppm&lt;br /&gt;
	       or tonumber(args.default_viewport) or 0.1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- convert from geohack dim (knowing the viewpoint size on screen) to geohack scale&lt;br /&gt;
local function geohackDimToScale(dim, args)&lt;br /&gt;
	dim = tonumber(dim)&lt;br /&gt;
	args = args or {}&lt;br /&gt;
	if not dim or dim &amp;lt;= 0 then return end&lt;br /&gt;
	local units = args.units&lt;br /&gt;
	if units and string.lower(units) == &amp;#039;km&amp;#039; then&lt;br /&gt;
		dim = dim*1000&lt;br /&gt;
	end&lt;br /&gt;
	return dim / computeViewport(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- inverse of above function, returning dim in km&lt;br /&gt;
local function geohackScaleToDim(scale, args)&lt;br /&gt;
	scale = tonumber(scale)&lt;br /&gt;
	args = args or {}&lt;br /&gt;
	if not scale or scale &amp;lt;= 0 then return end&lt;br /&gt;
	return scale * computeViewport(args) * 1e-3&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local oddShape = 2.09 --- length/sqrt(area) of Boston (to choose an example)&lt;br /&gt;
&lt;br /&gt;
-- Convert from Geohack&amp;#039;s types to Geohack dim&lt;br /&gt;
local function geohackTypeToDim(args)&lt;br /&gt;
	local t = args.type&lt;br /&gt;
    if not t then return end&lt;br /&gt;
	local typeDim = mw.loadData(&amp;#039;Module:Infobox_dim/data&amp;#039;)&lt;br /&gt;
	local dim = typeDim[t]&lt;br /&gt;
	local population = tonumber(args.population)&lt;br /&gt;
	if t == &amp;#039;city&amp;#039; and population and population &amp;gt; 0 then&lt;br /&gt;
		-- assume city is a circle with density of 1000/square kilometer&lt;br /&gt;
		-- compute diameter, in meters. Then multiply by oddShape to account for weird shapes&lt;br /&gt;
		dim = 35.68e-3*math.sqrt(population)*oddShape&lt;br /&gt;
		-- don&amp;#039;t zoom in too far&lt;br /&gt;
		if dim &amp;lt; 5 then&lt;br /&gt;
			dim = 5&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return dim		&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Convert from dimension of object to Geohack dim&lt;br /&gt;
local function computeDim(length,width,area)&lt;br /&gt;
	if length and width then&lt;br /&gt;
		return math.max(length,width)&lt;br /&gt;
	end&lt;br /&gt;
	if length then return length end&lt;br /&gt;
	if width then return width end&lt;br /&gt;
	if area then return oddShape*math.sqrt(area) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- compute geohack dim from unit arguments (e.g., length_mi)&lt;br /&gt;
local function convertDim(args)&lt;br /&gt;
	local length = args.length_mi and 1.60934*args.length_mi or args.length_km&lt;br /&gt;
	local width = args.width_mi and 1.60934*args.width_mi or args.width_km&lt;br /&gt;
	local area = args.area_acre and 0.00404686*args.area_acre or &lt;br /&gt;
		args.area_ha and 0.01*args.area_ha or &lt;br /&gt;
		args.area_mi2 and 2.58999*args.area_mi2 or args.area_km2&lt;br /&gt;
	local dim = computeDim(length, width, area)&lt;br /&gt;
	return dim&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function computeScale(args)&lt;br /&gt;
	if args.scale then return args.scale end&lt;br /&gt;
	local dim, units, scale&lt;br /&gt;
	if args.dim then&lt;br /&gt;
		dim, units = mw.ustring.match(args.dim,&amp;quot;^([-%d%.]+)%s*(%D*)&amp;quot;)&lt;br /&gt;
		args.units = units&lt;br /&gt;
		args.default_viewport = 0.1  -- default geohack viewpoirt&lt;br /&gt;
		scale = geohackDimToScale(dim, args)&lt;br /&gt;
	end&lt;br /&gt;
	if not scale then&lt;br /&gt;
		dim = convertDim(args) or geohackTypeToDim(args)&lt;br /&gt;
		args.units = &amp;#039;km&amp;#039;&lt;br /&gt;
		args.default_viewport = 0.2 --- when object dimensions or type is specified, assume 20cm viewport&lt;br /&gt;
		scale = dim and geohackDimToScale(dim, args)&lt;br /&gt;
	end&lt;br /&gt;
	if not scale then return end&lt;br /&gt;
	scale = math.floor(scale+0.5)&lt;br /&gt;
	-- keep scale within sane bounds&lt;br /&gt;
	if scale &amp;lt; 2000 then&lt;br /&gt;
		scale = 2000&lt;br /&gt;
	end&lt;br /&gt;
	if scale &amp;gt; 250e6 then&lt;br /&gt;
		scale = 250e6&lt;br /&gt;
	end&lt;br /&gt;
	return scale&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Argument checking&lt;br /&gt;
local positiveNumericArgs = {viewport_cm=true,viewport_px=true,length_mi=true,length_km=true,&lt;br /&gt;
                             width_mi=true,width_km=true,area_mi2=true,area_km2=true,&lt;br /&gt;
                             area_acre=true,area_ha=true,scale=true,population=true}&lt;br /&gt;
&lt;br /&gt;
local function cleanArgs(args)&lt;br /&gt;
    local clean = {}&lt;br /&gt;
    if type(args) == &amp;#039;table&amp;#039; then&lt;br /&gt;
        for k, v in pairs(args) do&lt;br /&gt;
            if positiveNumericArgs[k] then&lt;br /&gt;
                v = v and mw.ustring.gsub(v,&amp;quot;,&amp;quot;,&amp;quot;&amp;quot;) -- clean out any commas&lt;br /&gt;
                v = tonumber(v)                     -- ensure argument is numeric&lt;br /&gt;
                if v and v &amp;lt;= 0 then                -- if non-positive, ignore value&lt;br /&gt;
                    v = nil&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
            clean[k] = v&lt;br /&gt;
         end&lt;br /&gt;
    end&lt;br /&gt;
    return clean&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Module entry points&lt;br /&gt;
function p._dim(args)&lt;br /&gt;
    args = cleanArgs(args)&lt;br /&gt;
	if args.dim then return args.dim end&lt;br /&gt;
	-- compute scale for geohack&lt;br /&gt;
	local scale = args.scale&lt;br /&gt;
	local dim&lt;br /&gt;
	if not scale then&lt;br /&gt;
		args.default_viewport = 0.2 -- when specifying a object dimension or type, assume output spans 20cm&lt;br /&gt;
		dim = convertDim(args) or geohackTypeToDim(args)&lt;br /&gt;
		args.units = &amp;#039;km&amp;#039;&lt;br /&gt;
		scale = dim and geohackDimToScale(dim, args)&lt;br /&gt;
	end&lt;br /&gt;
	-- reset back to 10cm viewport for correct geohack dim output&lt;br /&gt;
	args.viewport_cm = 10&lt;br /&gt;
	dim = scale and geohackScaleToDim(scale, args)&lt;br /&gt;
	return dim and tostring(math.floor(dim+0.5))..&amp;#039;km&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._scale(args)&lt;br /&gt;
    args = cleanArgs(args)&lt;br /&gt;
    return computeScale(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._zoom(args)&lt;br /&gt;
    args = cleanArgs(args)&lt;br /&gt;
	args.viewport_px = args.viewport_px or 200 --- viewport for Kartographer is 200px high&lt;br /&gt;
	local scale = computeScale(args)&lt;br /&gt;
	if scale then&lt;br /&gt;
		local zoom = geohackScaleToMapZoom(scale)&lt;br /&gt;
		return zoom and math.floor(zoom)&lt;br /&gt;
	end&lt;br /&gt;
end		&lt;br /&gt;
&lt;br /&gt;
-- Template entry points&lt;br /&gt;
function p.dim(frame)&lt;br /&gt;
	return p._dim(getArgs(frame)) or &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.scale(frame)&lt;br /&gt;
	return p._scale(getArgs(frame)) or &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.zoom(frame)&lt;br /&gt;
	return p._zoom(getArgs(frame)) or &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>&gt;Hike395</name></author>
	</entry>
</feed>