Módulo:Mapa

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Saltar para a navegação Saltar para a pesquisa

A documentação para este módulo pode ser criada na página Módulo:Mapa/doc

local p = {}
local pointmod = require('Módulo:Mapa/Pontos')
local linguistic = require('Módulo:Linguística')
local maintenance = ''
local coord  = require('Módulo:Coordenadas')
local wd = require('Módulo:Infobox/Wikidata')
local function loaddata(name)
				return require('Módulo:Mapa/dados/' .. mw.ustring.lower(name))
end
local divstyle = {
				['clear'] = 'right',
				['width'] ='auto',
				['text-align'] = 'center',
				['font-size'] = '0.9em',
				['line-height'] = '1.4em',
				['margin'] = '0 0 0.5em 1em',
				['max-width'] = '325px',
				['word-wrap'] = 'break-word',
				['max-width'] = '99%',
				['height'] = 'auto',
				['justify-content'] = 'space-around',
				['align-items'] = 'center',
}
local function addmaintenancecat(cat, sortkey) -- adicione texto à string de manutenção se houver um problema
				if mw.title.getCurrentTitle().namespace ~= 0 then
				return
				end
				maintenance = maintenance .. '[[Categoria:' .. cat .. ']]'
end
-- 'Projeção cônica com DL'
local function dllat(latitude, longitude, mapdata) -- cônica com DL
				local val =
					(mapdata.y0 +
									( mapdata.iheight/2 - mapdata.y0 ) *
									(1 - mapdata.t *
									(latitude - mapdata.centrallat) *
									(0.01745329252 + 0.00000177219231 * (latitude - mapdata.centrallat) ^2)
									)*
									( 1- 0.00015230871 * (longitude-mapdata.centrallong) ^2 * mapdata.s^2)
					) / mapdata.iheight
				return val
end
local function dllong(latitude, longitude, mapdata)
				local val =
				( (mapdata.x0 or (mapdata.iwidth/2)) +
								( mapdata.iheight/2 - mapdata.y0 ) *
								( 1 -mapdata.t *
								(latitude-mapdata.centrallat) *
								( 0.01745329252 + 0.00000177219231 * (latitude-mapdata.centrallat) * (latitude-mapdata.centrallat) )
								) *
				(longitude-mapdata.centrallong) * mapdata.s *
				(0.01745329252 - 0.000000886096156 * (longitude-(mapdata.centrallong)) * (longitude-(mapdata.centrallong))
								* mapdata.s^2
				)
				) / mapdata.iwidth
				return val
end
--longitude equirectangular
local function equirecLong(longitude, mapdata)
				local left, right = mapdata.left, mapdata.right
				if right < left then -- se o mapa passa no meridiano 180
				right = 360 + right
				if longitude < 0 then longitude = (360 + longitude) end
				end
				return  (longitude - left) / (right - left)
end
local function equirecLat(latitude, mapdata)
				return (latitude - mapdata.top) / (mapdata.bottom - mapdata.top)   
end
----------------------------------
local function numericcoord(val) -- digitaliza coordenadas que, às vezes, estão na forma de grau/min/seg
				return tonumber(val) or tonumber(coord.dms2dec({args={val}}))
end
local function pointposition(latitude, longitude, mapdata)
				if not (latitude and longitude) then
				return nil --?
				end
		
				if longitude > 180 then -- os caprichos das coordenadas extraterrestres
				longitude = -360 + longitude
				elseif longitude < -180 then
				longitude = 360 - longitude
				end
						
				local ypos, xpos
				if mapdata.x and mapdata.y then -- para mapas complexos: calculando a posição das fórmulas no Wikicode nas chaves "x" e "y"
				xpos = mapdata.x(latitude, longitude) / 100
				ypos = mapdata.y(latitude, longitude) / 100
				elseif mapdata.projection ==  'Projeção equiretangular' or mapdata.projection ==  'Projeção equirectangular' then
				ypos = equirecLat(latitude, mapdata)
				xpos =    equirecLong(longitude, mapdata)
				elseif mapdata.projection == 'Projeção cônica com DL' then
				ypos = dllat(latitude, longitude, mapdata)
				xpos = dllong(latitude, longitude, mapdata)
				end
				return ypos, xpos
end
local function placepoint(mapdata, point) -- função ange para o ponto buildmap é uma tabela contendo latitude, longitude, tipo de ponto ...
				local ypos, xpos = pointposition(point.latitude, point.longitude, mapdata)
				if (not xpos) or (not ypos) then
				return "dados de localização inválidos"
				end
				if (ypos > 1.1) or (xpos) > 1.1 or (ypos < -0.1) or (xpos < -0.1) then
				return '[[Categoria:!Artigos com coordenadas fora do mapa]]' .. 'as coordenadas mostradas estão fora do mapa de localização solicitado'
				end
		
				local pointsize = tostring(point.pointsize or '8')
				local pointtext = point.text or ''
				local pointtype = point.pointtype or 'default'
				local pointimage = pointmod[pointtype]
				if not pointimage then
				pointimage = pointmod.default
				addmaintenancecat('!Páginas com uma predefinição de ponto de mapa não suportados')
				end
				local htmlheight = tostring(ypos * 100) .. '%'
				local htmlwidth = tostring(xpos * 100) .. '%'
				local pointdiv = mw.html.create('div')
				:css{position = 'absolute', border = 'none', top = htmlheight, left = htmlwidth}
				:tag('div')
								:css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0', width = '8px'}
								:wikitext('[[Image:' .. pointimage .. '|' .. pointsize .. 'px|class=noviewer]]')
								:tag('span')
								:css{position = 'absolute', ['text-align'] = 'left', width = '150px'}
								:wikitext(pointtext)
								:done()
				:allDone()
						
				return pointdiv
end
local function buildmap(file, caption, alt, width, mapdata, pointtable, defaultpoint)
				local map = mw.html.create('div')
				:css(divstyle)
				:addClass("geobox")
				:wikitext(caption)
				:tag('table')
								:addClass('InicioMapa')
								:attr({border="0", cellspacing="0", cellpadding="0"})
								:css({margin = '0', border = 'none', padding = '0', width = 'auto'})
								:tag('tr')
												:tag('td')
												:tag('div')
																:css({position= 'relative', margin = "auto", width = '100%', ['text-align'] = 'right' })
																:wikitext('[[File:' .. file .. '|frameless|' .. width .. 'px' .. '|' .. alt .. '|class=noviewer]]' )
		
				for i, j in pairs(pointtable or{}) do -- para verficar ponto a colocar, do
				if not j.pointtype then
								j.pointtype = defaultpoint
				end
				map:node(placepoint(mapdata,j))
				end
				return map:done():done():done():done()
end
local function maxpoints(points)
				if not points then
				return nil
				end
				local minlat, maxlat, minlong, maxlong = points[1].latitude, points[1].latitude, points[1].longitude, points[1].longitude
				for i, point in ipairs(points) do
				minlat = math.min(point.latitude, minlat)
				maxlat = math.max(point.latitude, maxlat)
				minlong = math.min(point.longitude, minlong)
				maxlong = math.max(point.longitude, maxlong)
				end
				return minlat, maxlat, minlong, maxlong
				end
local function guesszoom(ids)
				if (not ids) then
				return nil
				end
				local item = ids[1]
				local area = wd.formatStatements{entity = item, property = "P2046", targetunit = "square kilometer", displayformat = "raw"}
				if (not area) or not(tonumber(area)) then
				return nil
				end
				area = tonumber(area)
				if area > 100000 then
				return 3
				end
				if area > 10000 then
				return 4
				end
				if area > 1000 then
				return 5
				end
				if area > 100 then
				return 6
				end
				return 7
end
local function guesszoom2(minlat, maxlat, minlong, maxlong)
				if not (minlat and maxlat and minlong and maxlong) or ((minlat == maxlat) and (minlong == maxlong) ) then
				return nil
				end
				local x = coord._distance({latitude = (maxlat + minlat / 2), longitude = minlong}, {latitude = (maxlat + minlat / 2), longitude =  maxlong })
				local y = coord._distance({latitude = 0, longitude = minlong}, {latitude = 0, longitude = maxlong})
				local dist = math.max(x, y) -- para ajustar se o mapa não é quadrado
				if (dist > 512) then
				return 4
				elseif (dist > 256) then
				return 5      
				elseif (dist > 128) then
				return 6
				elseif (dist> 64) then
				return 7
				elseif (dist > 32) then
				return 8
				elseif (dist > 16) then
				return 9
				elseif (dist > 8) then
				return 10
				elseif (dist > 4) then
				return 11
				elseif (dist > 2) then
				return 12
				end
				return 13
end
local function buildInteractiveMap(width, pointtable, default_zoom, ids, shapecolor)
				-- Fazemos um hack para gerar um valor padrão para latitude e longitude
				local geojson = {}
				shapecolor = shapecolor or '#800000'
				for i, point in pairs(pointtable or{}) do -- para cada ponto colocar, do
				table.insert(geojson, {
								['type'] = 'Feature',
								['geometry'] = {
								['type'] = "Point",
								['coordinates'] = { point.longitude, point.latitude }
								},
								['properties'] = {
								['title'] = point.text or '',
								['marker-symbol'] = point.marker,
								['marker-color'] =  point.markercolor or "#224422",
								}
				})
				if ids then
				local geojson2 = {
				['type'] = 'ExternalData',
								['service'] = 'geoshape',
						['ids'] = ids,
						properties = {
								['fill'] = shapecolor or '#800000',
						},
				}
				table.insert(geojson, geojson2)
				end
				end
		
				local minlat, maxlat, minlong, maxlong = maxpoints(pointtable)
				local center_lat = (maxlat + minlat) / 2
				local center_long = (maxlong + minlong) / 2
				-- 180e méridien
				if (maxlong - minlong) > 180 then
				centerlong = centerlong - 180
				end
				local args = {
								['height'] = width,
								['width'] = width,
								['frameless'] = 'frameless',
								['align'] = 'center',
								['latitude'] = center_lat,
								['longitude'] = center_long,
								['zoom'] = default_zoom or guesszoom(ids) or guesszoom2(minlat, maxlat, minlong, maxlong) or 13
				}
				return mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), args)
end
local function builddynamicmap(map, maptype, width, pointtable, caption, defaultpoint, globe, default_zoom, ids, shapecolor) -- função de ajuda para multimap
				if map == '-' then
				return
				end
				if map == 'interactive' then
				if (globe and (globe ~= 'earth')) then
								return nil
				end
				return buildInteractiveMap(width, pointtable, default_zoom, ids, shapecolor)
				end
				local success, mapdata = pcall(loaddata, map)
				if not success or not mapdata.images then
				addmaintenancecat('!Páginas com dados de localização não suportados')
				end
				local name = mapdata.name or '?'
				-- análise linguística para o texto do mapa
				local datagender = mapdata.genre or ''
				local gender = string.sub(datagender, 1, 1) -- ms = masculino-singular, fp = feminino plural etc.
				local number = string.sub(datagender, 2, 2)
				local determiner = mapdata.determiner
				local ofstring = linguistic.of(name, gender, number, determiner) -- restaura "da França" ou "do Japão"
				local mapname = mapdata.name
				local file = mapdata.images[maptype] or mapdata.images['default']  or mapdata.images[1]
				if not file then
					file = mapdata.images['default']
				end
				local alt = 'ver no mapa ' .. ofstring
				local caption = 'Localização no mapa ' .. ofstring
				return buildmap(file, caption, alt, width, mapdata, pointtable, defaultpoint)
end
local function guessmaps(params)
				-- cas non terriens
				local globe = params.globe
				if globe and (globe ~= 'earth') then
				local maps = {
								moon = 'Lua',
								mars = 'Marte',
								mercury = 'Mercúrio',
								neptune = 'Neptuno',
								venus = 'Vénus',
								callisto = 'Calisto',
								ceres = 'Ceres',
								charon = 'Charon',
								enceladus = 'Encelade',
								europa = 'Europa',
								io = 'Io',
								iapetus = 'Japet',
								ganymede = 'Ganimede',
								pluto = 'Plutão',
								tethys = 'Tétis',
								titan = 'Titan',
								triton = 'Triton',
								vesta = 'Vesta',
				}
				return maps[mw.ustring.lower(globe)]
				end
		
				-- outros casos
				local data = require('Módulo:Mapa/dados')
				local validmaps = {}
				local lat = tonumber(params.latitude) or tonumber(coord.dms2dec({args={params.latitude}}))
				local long = tonumber(params.longitude) or tonumber(coord.dms2dec({args={params.longitude}}))
				if not lat or not long then return nil end
				for i, map in pairs(data) do
				if lat < map.top and lat > map.bottom then
								if map.left > map.right  then -- correção para meridiano de passagem de mapas 180
								if long < 0 then
												map.left = map.left - 360
								else
												map.right = 360 + map.right
								end
								end
								if (long > map.left and long < map.right) then
								table.insert(validmaps, map)
								end
				end
				end
				if #validmaps == 0 then
				return nil
				end
		
				local function area(map) -- função simples só para poder classificar apreciavelmente os mapas não superficiais
				return (math.abs(map.top - map.bottom)) * (math.abs(map.left- map.right))
				end
		
				table.sort(validmaps, function(a, b) return area(a) < area(b) end)
				local chosenmaps = {} -- nós não mantemos todos eles, muitas vezes seria demais
				local havezone = {} -- parâmetro "zona" do mapa já obtido, para não ter os mesmos dois tempos: { zone = {nome do mapa, posição do mapa} }
				local forbiddenzones = { -- Zona não é útil, não usar por padrão
				['futura região francesa'] = true,
				['frança'] = true, -- usado para áreas não administrativas, geneticamente não prático
				['itália'] = true,
				}
				local function addmap(map, pos) -- adicione o mapa à lista e registe que ele tem um mapa com esse parâmetro "zone"
				if pos then
								chosenmaps[pos] = map.name
								havezone[map.zone] = {map, pos}
				else
								table.insert(chosenmaps, map.name)
								if map.zone then
								havezone[map.zone] = {map, #chosenmaps}
								end
				end
				end
				local function centrality(map)
				-- retorna um índice de centralidade ah hoc, mais fraco se o ponto estiver perto de uma borda
				local function compute(point, end1, end2)
								local pct = (point - end1) / (end2- end1)
								return 0.5 - math.abs(0.5 - pct)
				end
				local latcentrality = compute(lat, map.top, map.bottom)
				local longcentrality = compute(long, map.left, map.right)
				return math.min(latcentrality, longcentrality)
				end
				for i, map in pairs(validmaps) do
				if not(havezone[map.zone]) and    (not forbiddenzones[map.zone]) and #chosenmaps < 3 then
								addmap(map)
				end
				if map.zone and havezone[map.zone] and (centrality(map) > centrality(havezone[map.zone][1] ))  then -- se dois mapas tiverem o mesmo parâmetro "zona", nós tomamos o melhor centralizado
								addmap(map, havezone[map.zone][2])
				end
				end
				addmaintenancecat('!Páginas com mapas')
				return chosenmaps
end
function p.multimap(params)
				local maplist = params.maplist
				local globe = params.globe
				if not maplist and params.guessmaps ~= '-' then -- Os guessmaps podem ter outros parâmetros (escala, etc.)
				maplist = guessmaps(params)
				end
				if type(maplist) == 'string' then
				if maplist == 'não' or maplist == 'não pertinente' or maplist == 'nao' then
								return
				elseif maplist == 'interactive' and globe and globe ~= 'earth' then
								maplist = guessmaps(params)
				end
				maplist = mw.text.split(maplist, '/', true)
				end
				local staticmaps = params.staticmaps
				if type(staticmaps == 'string') then
				staticmaps = {staticmaps}
				end
				if (not maplist)  and (not staticmaps) then
				return nil
				end
				local maptype = params.maptype -- retrabalhar para quando queremos a mesma região, mas com vários tipos de mapa
				local width = params.width
				local pointtable = params.pointtable
				local caption = params.caption
				local defaultpoint = params.pointtype
				local default_zoom = params.default_zoom
				local ids = params.ids -- wikidata ids
				if not pointtable then -- tabela de pontos é a lista de pontos a serem colocados, mas quando há apenas um, podemos simplesmente ter latitude e longitude
				pointtable = {{latitude = params.latitude, longitude = params.longitude, marker = params.marker, markercolor = params.markercolor}}
				end
				for i=#pointtable, 1, -1 do
				local point = pointtable[i]
				point.latitude =  numericcoord(point.latitude)
				point.longitude =  numericcoord(point.longitude)
				if not ( point.latitude and point.longitude ) then
								table.remove( pointtable, i )
				end
				point.markercolor = point.markercolor or params.markercolor
				point.marker = point.marker or params.marker
				end
				if #pointtable == 0 then
				return
				end
				-- tratamento de largura
				if width and tonumber(width) then
				width = tonumber(width)
				else
				width = 280
				end-- se não for um número, erro ?
				local div =     mw.html.create('div'):addClass('img_toogle')
		
					--transição: veja aux [[Predefinição:Geolocalização/]] n na ausência de dados no módulo
				for i, j in ipairs(maplist or {}) do
				if j == '' then break end
				if j ~= 'interactive' then
								local success, data = pcall(loaddata, j)
								if not success then
									local mapliststring = table.concat(maplist, '/')
								return mw.getCurrentFrame():expandTemplate{title = 'Infobox/Geolocalização múltipla/transição', args = {['localizacao'] = mapliststring, type = maptype, latitude = params.latitude or pointtable[1].latitude, longitude = params.longitude or pointtable[1].longitude}}
												.. '[[Categoria:!Páginas com dados de localização não suportados]]'
								end
				end
				end
				for i = #maplist, 1, -1 do
				if maplist[i] ~= '' then
								local newmap = builddynamicmap( maplist[i], maptype, width, pointtable, caption, defaultpoint, globe, default_zoom, ids, params.shapecolor)
								div:node(newmap)
								div:tag('span'):wikitext(maintenance)
				end
				end
				for i, file in pairs(staticmaps or {}) do
				if j == '' then break end
				local caption = "Mapa de localização"
				local alt = "Ver o mapa detalhado"
				local newmap = buildmap( file, caption, alt, width)
				div:node(newmap)
				div:tag('span'):wikitext(maintenance)
				end
				return tostring(div)
end
function p.map(frame)
				local args = frame.args
				-- utilização de português
				args.maplist =  mw.text.split( args.mapa, '/', true)
				return p.multimap(args)
end
return p