Módulo:Mapa
Descrição
Módulo para gerar mapas
Uso
Use a função pontos para gerar um mapa com as coordenadas de vários pontos. As coordenadas devem ser dadas no formato decimal, separado apenas por um espaço, longitude antes da latitude.
Ver também
- {{Mapa dinâmico}}
- {{Mapa de localização}}
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