local p = {}
local pointmod = require('Module:Carte/Points')
local linguistic = require('Module:Linguistique')
local maintenance = ''
local coord = require('Module:Coordinates')
local function loaddata(name)
return require('Module:Carte/données/' .. 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) -- ajoute du texte à la string maintenance en cas de problème
if mw.title.getCurrentTitle().namespace ~= 0 then
return
end
maintenance = maintenance .. '[[Category:' .. cat .. ']]'
end
-- 'Projection conique avec DL'
local function dllat(latitude, longitude, mapdata) -- conique avec 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 équirectangulaire
local function equirecLong(longitude, mapdata)
local left, right = mapdata.left, mapdata.right
if right < left then -- si la carte passe le méridien 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) -- met en format numériques les coordonnées qui sont parfois sous la forme degré/min/sec
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 -- les caprices des coordonnées extraterrestres
longitude = -360 + longitude
elseif longitude < -180 then
longitude = 360 - longitude
end
local ypos, xpos
if mapdata.x and mapdata.y then -- pour les cartes complexes : calcul de la position à partir de formules en Wikicode dans les clés "x" et "y"
xpos = mapdata.x(latitude, longitude) / 100
ypos = mapdata.y(latitude, longitude) / 100
elseif mapdata.projection == 'Projection équirectangulaire' then
ypos = equirecLat(latitude, mapdata)
xpos = equirecLong(longitude, mapdata)
elseif mapdata.projection == 'Projection conique avec DL' then
ypos = dllat(latitude, longitude, mapdata)
xpos = dllong(latitude, longitude, mapdata)
end
return ypos, xpos
end
local function placepoint(mapdata, point) -- fonction d'aige pour buildmap point est une table contenant latitude, longitude, type de point...
local ypos, xpos = pointposition(point.latitude, point.longitude, mapdata)
if (not xpos) or (not ypos) then
return "données de géolocalisation invalides"
end
if (ypos > 1.1) or (xpos) > 1.1 or (ypos < -0.1) or (xpos < -0.1) then
return '[[Category:Article avec une géolocalisation hors-carte]]' .. 'les coordonnées indiquées sont hors de la carte de géolocalisation demandée'
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('Page avec un modèle de point de carte non pris en charge')
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('DebutCarte')
: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 -- pour chque point à placer, 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 buildInteractiveMap(width, pointtable, default_zoom, ids, shapecolor)
-- On fait un hack pour générer une valeur par défaut pour la latitude et la longitude
local geojson = {}
local centerlat, centerlong, minlat, minlong, maxlat, maxlong
-- determine appropriate zoom level and center for multi-point maps ? are there cleaner way to do that ?
if pointtable then
minlat, maxlat, minlong, maxlong = pointtable[1].latitude, pointtable[1].latitude, pointtable[1].longitude, pointtable[1].longitude
for i, point in ipairs(pointtable) do
if point.latitude < minlat then
minlat = point.latitude
elseif point.latitude > maxlat then
maxlat = point.latitude
end
if point.longitude < minlong then
minlong = point.longitude
elseif point.longitude > maxlong then
maxlong = point.longitude
end
end
centerlat = (maxlat + minlat) / 2
centerlong = (maxlong + minlong) / 2
-- 180e méridien
if (maxlong - minlong) > 180 then
centerlong = centerlong - 180
end
local maxdim = math.max((maxlat - maxlong), (minlat - minlong))
if maxdim > 1000 then
default_zoom = 3
elseif maxdis > 100 then
default_zoom = 8
end
end
shapecolor = shapecolor or '#800000'
for i, point in pairs(pointtable or{}) do -- pour chque point à placer, 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 args = {
['height'] = width,
['width'] = width,
['frameless'] = 'frameless',
['align'] = 'center',
['latitude'] = center_lat,
['longitude'] = center_lon,
['zoom'] = default_zoom 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) -- fonction d'aide pour 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('Page avec des données de géolocalisation non prises en charge')
end
local name = mapdata.name or '?'
-- analyse linguistique pour le texte de la carte
local datagender = mapdata.genre or ''
local gender = string.sub(datagender, 1, 1) -- ms = masculin-singulier, fp = féminin pluriel etc.
local number = string.sub(datagender, 2, 2)
local determiner = mapdata.determiner
local ofstring = linguistic.of(name, gender, number, determiner) -- restitue "de France" ou "du Japon"
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 = 'voir sur la carte ' .. ofstring
local caption = 'Localisation sur la carte ' .. 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 = 'Lune',
mars = 'Mars',
mercury = 'Mercure',
neptune = 'Neptune',
venus = 'Vénus',
callisto = 'Callisto',
ceres = 'Cérès',
charon = 'Charon',
enceladus = 'Encelade',
europa = 'Europe',
io = 'Io',
iapetus = 'Japet',
ganymede = 'Ganymède',
pluto = 'Pluton',
tethys = 'Téthys',
titan = 'Titan',
triton = 'Triton',
vesta = 'Vesta',
}
return maps[mw.ustring.lower(globe)]
end
-- autres cas
local data = require('Module:Carte/données')
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 -- correction pour les cartes passant le méridien 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) -- fonction simple juste pour pouvoir classer apprximativement les cartes pas superficie
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 = {} -- on ne les gardes pas toutes, ça serait souvent trop
local havezone = {} -- paramètre "zone" des carte déjà obtenu, pour ne pas avoir le même deux fois: { zone = {nom de la carte, position de la carte} }
local forbiddenzones = { -- zone peu utiles, à ne pas utiliser par défaut
['future région française'] = true,
['france'] = true, -- utilisé pour des zone non administratives, généalement pas pratique
['italie'] = true,
}
local function addmap(map, pos) -- ajoute la carte à la liste et enregistre qu'elle a une carte avec ce paramètre "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)
-- retourne un indice ah hoc de centralité, plus faible si le point est près d'un bord
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 -- si deux cartes ont le même paramètre "zone", on prend la mieux centrée
addmap(map, havezone[map.zone][2])
end
end
addmaintenancecat('Carte de localisation ajoutée par Module:Carte')
return chosenmaps
end
function p.multimap(params)
local maplist = params.maplist
local globe = params.globe
if not maplist and params.guessmaps ~= '-' then -- guessmaps pourrait prendre d'autres paramètres (échelle, etc.)
maplist = guessmaps(params)
end
if type(maplist) == 'string' then
if maplist == 'non' or maplist == 'pas pertinent' or maplist == 'non' 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 -- à retravailler pour quand on veut la même région, mais avec plusieurs types de carte
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 -- pointtable est la liste des points à placer, mais lorsqu'il n'y a qu'un seul, on peut simplement avoir latitude et 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
-- traitement de la largeur
if width and tonumber(width) then
width = tonumber(width)
else
width = 280
end-- si pas un nombre, erreur ?
local div = mw.html.create('div'):addClass('img_toggle')
-- met la carte à afficher par défaut à la fin, pour qu'elle soit affichée en premier
if maplist then
table.insert(maplist, maplist[1])
table.remove(maplist, 1)
end
--transition: appel aux [[Modèle:Géolocalisation/]] n en l'absence de données dans le module
for i, j in pairs(maplist or {}) do
if j == '' then break end
if j ~= 'interactive' then
local success, data = pcall(loaddata, j)
if not success then
local mapliststring = ''
for i, j in pairs(maplist) do
mapliststring = j .. '/' .. mapliststring
end
return mw.getCurrentFrame():expandTemplate{ title = 'Infobox/Géolocalisation multiple/transition', args = {['géolocalisation'] = mapliststring, type = maptype, latitude = params.latitude, longitude = params.longitude }}
.. '[[Catégorie:Page avec des données de géolocalisation non prises en charge]]'
end
end
end
for i, j in pairs(maplist or {}) do
if j == '' then break end
local newmap = builddynamicmap( j, maptype, width, pointtable, caption, defaultpoint, globe, default_zoom, ids, params.shapecolor)
div:node(newmap)
div:tag('span'):wikitext(maintenance)
end
for i, file in pairs(staticmaps or {}) do
if j == '' then break end
local caption = "Carte de localisation"
local alt = "Voir la carte détaillée"
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
-- utilisation du franaçs
args.maplist = mw.text.split( args.carte, '/', true)
return p.multimap(args)
end
return p