Documentação do módulo[ver] [editar] [histórico] [purgar]

Descrição editar

Módulo para gerar mapas

Uso editar

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 editar

local p = {}
local pointmod = require('Módulo:Mapa/Pontos')
local linguistic = require('Módulo:Linguística')
local maintenance = ''
local coord  = require('Módulo:Coordenadas/Testes')
local wd = require('Módulo:Infobox/Wikidata/Testes')
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