Module:Carte : Différence entre versions

De Lagny-sur-Marne Wiki
Aller à : navigation, rechercher
(+ doc)
(passe toutes les coordonnées en format numérique dès le début pour compatibilité avec les cartes intéractives (Utilisateur:Zolo/test))
 
(56 révisions intermédiaires par 8 utilisateurs non affichées)
Ligne 2 : Ligne 2 :
 
local pointmod = require('Module:Carte/Points')
 
local pointmod = require('Module:Carte/Points')
 
local linguistic = require('Module:Linguistique')
 
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 p = {}
+
local function addmaintenancecat(cat, sortkey) -- ajoute du texte à la string maintenance en cas de problème
 
+
if mw.title.getCurrentTitle().namespace ~= 0 then
local function loaddata(name)  
+
return
return require('Module:Carte/données/' .. string.lower(name))
+
end
 +
maintenance = maintenance .. '[[Category:' .. cat .. ']]'
 
end
 
end
  
 
-- 'Projection conique avec DL'   
 
-- 'Projection conique avec DL'   
 
local function dllat(latitude, longitude, mapdata) -- conique avec DL
 
local function dllat(latitude, longitude, mapdata) -- conique avec DL
local val = 100*
+
local val =  
 
(mapdata.y0 +
 
(mapdata.y0 +
 
( mapdata.iheight/2 - mapdata.y0 ) *
 
( mapdata.iheight/2 - mapdata.y0 ) *
Ligne 24 : Ligne 44 :
 
return val
 
return val
 
end
 
end
 +
 
local function dllong(latitude, longitude, mapdata)
 
local function dllong(latitude, longitude, mapdata)
local val = 100 * (
+
local val =  
 
( (mapdata.x0 or (mapdata.iwidth/2)) +  
 
( (mapdata.x0 or (mapdata.iwidth/2)) +  
 
( mapdata.iheight/2 - mapdata.y0 ) *
 
( mapdata.iheight/2 - mapdata.y0 ) *
Ligne 36 : Ligne 57 :
 
* mapdata.s^2
 
* mapdata.s^2
 
)
 
)
)
 
 
) / mapdata.iwidth
 
) / mapdata.iwidth
 
return val  
 
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
 
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 buildmap(map, maptype, width, latitude, longitude, pointtype, caption) -- fonction d'aige pour buildmap
+
local function pointposition(latitude, longitude, mapdata)
if map == '-' then
+
if not (latitude and longitude) then
return
+
return nil --?
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 supportées')
 
 
end
 
end
local name = mapdata.name or '?'
 
 
 
if (not pointtype) then
+
if longitude > 180 then -- les caprices des coordonnées extraterrestres
pointtype = default
+
longitude = -360 + longitude
 +
elseif longitude < -180 then
 +
longitude = 360 - longitude
 
end
 
end
pointimage = pointmod[pointtype]
+
if not pointimage then pointimage = pointmod.default end -- + message d'erreur ?
+
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"
-- analyse linguistique pour le texte de la carte
+
xpos = mapdata.x(latitude, longitude) / 100
local datagender = mapdata.genre or ''
+
ypos = mapdata.y(latitude, longitude) / 100
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 pointsize = tostring(8)
 
local ypos, xpos
 
if mapdata.x and mapdata.y then
 
ypos, xpos = mapdata.y, mapdata.x
 
 
ypos = string.gsub(ypos, '$lat', tostring(latitude))
 
ypos = string.gsub(ypos, '$long', tostring(longitude))
 
ypos = tonumber(mw.ext.ParserFunctions.expr(ypos))
 
xpos = string.gsub(xpos, '$lat', tostring(latitude))
 
xpos = string.gsub(xpos, '$long', tostring(longitude))
 
xpos = tonumber(mw.ext.ParserFunctions.expr(xpos))
 
 
 
elseif mapdata.projection ==  'Projection équirectangulaire' then
 
elseif mapdata.projection ==  'Projection équirectangulaire' then
ypos = 100 *  (latitude - mapdata.top) / (mapdata.bottom - mapdata.top)  
+
ypos = equirecLat(latitude, mapdata)
xpos = 100 * (longitude - mapdata.left) / (mapdata.right - mapdata.left)  
+
xpos =  equirecLong(longitude, mapdata)
 
elseif mapdata.projection == 'Projection conique avec DL' then
 
elseif mapdata.projection == 'Projection conique avec DL' then
 
ypos = dllat(latitude, longitude, mapdata)
 
ypos = dllat(latitude, longitude, mapdata)
 
xpos = dllong(latitude, longitude, mapdata)
 
xpos = dllong(latitude, longitude, mapdata)
else
 
return "système cartographique non supporté"
 
 
end
 
end
if (ypos > 100) or (xpos) > 100 or (ypos < 0) or (xpos < 0) then  
+
return ypos, xpos
return '[[Category:Article avec une géolocalisation hors-carte]]' .. error(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 supporté')
 
end
 
end
local htmlheight = tostring(ypos) .. '%'
+
 
local htmlwidth = tostring(xpos) .. '%'
+
local htmlheight = tostring(ypos * 100) .. '%'
 +
local htmlwidth = tostring(xpos * 100) .. '%'
  
 
local pointdiv = mw.html.create('div')
 
local pointdiv = mw.html.create('div')
Ligne 97 : Ligne 134 :
 
:tag('div')
 
:tag('div')
 
:css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0', width = '8px'}
 
:css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0', width = '8px'}
:wikitext('[[Image:' .. pointimage .. ']]')
+
:wikitext('[[Image:' .. pointimage .. '|' .. pointsize .. 'px]]')
:done()
+
:tag('span')  
local mapname = mapdata.name
+
:css{position = 'absolute', ['text-align'] = 'left', width = '150px'}
local file
+
:wikitext(pointtext)
if type(mapdata.images) == 'string' then
+
:done()
file = mapdata.images
+
:allDone()
else
+
file = mapdata.images[maptype] or mapdata.images[1] or mapdata.images['default']
+
return pointdiv
end
+
end
local alt = 'voir sur la carte ' .. ofstring
+
 
 +
local function buildmap(file, caption, alt, width, mapdata, pointtable, defaultpoint)
 
local map = mw.html.create('div')
 
local map = mw.html.create('div')
:css{['text-align'] = 'center'}
+
:css(divstyle)
 
:addClass("geobox")
 
:addClass("geobox")
:wikitext('<small>Géolocalisation sur la carte ' .. ofstring .. '</small>')
+
:wikitext(caption)
 
:tag('table')
 
:tag('table')
 
:addClass('DebutCarte')
 
:addClass('DebutCarte')
 
:attr({border="0", cellspacing="0", cellpadding="0"})
 
:attr({border="0", cellspacing="0", cellpadding="0"})
:css({margin = '0', border = 'none', padding = '0'})
+
:css({margin = '0', border = 'none', padding = '0', width = 'auto'})
 
:tag('tr')
 
:tag('tr')
 
:tag('td')
 
:tag('td')
 
:tag('div')
 
:tag('div')
:css({position= 'relative', margin = "auto",})
+
:css({position= 'relative', margin = "auto", width = '100%', ['text-align'] = 'right' })
:wikitext('[[Fichier:' .. file .. '|' .. width .. 'px' .. '|' .. alt .. ']]' )
+
:wikitext('[[File:' .. file .. '|frameless|' .. width .. 'px' .. '|' .. alt .. ']]' )
:node(pointdiv)
+
:done()
+
for i, j in pairs(pointtable or{}) do -- pour chque point à placer, do
:done()
+
if not j.pointtype then
:done()
+
j.pointtype = defaultpoint
:done()
+
end
return map
+
map:node(placepoint(mapdata,j))
 +
end
 +
return map:done():done():done():done()
 +
end
 +
 
 +
local function buildInteractiveMap(width, pointtable, default_zoom, marker)
 +
if marker == nil then
 +
marker = ''
 +
end
 +
-- On fait un hack pour générer une valeur par défaut pour la latitude et la longitude
 +
local geojson = {
 +
['type'] = 'FeatureCollection',
 +
['features'] = {}
 +
}
 +
local center_lat = 0
 +
local center_lon = 0
 +
local point_count = 0
 +
for i, point in pairs(pointtable or{}) do -- pour chque point à placer, do
 +
if not point.pointtype then
 +
point.pointtype = defaultpoint
 +
end
 +
table.insert(geojson['features'], {
 +
['type'] = 'Feature',
 +
['geometry'] = {
 +
['type'] = "Point",
 +
['coordinates'] = { point.longitude, point.latitude }
 +
},
 +
['properties'] = {
 +
['title'] = point.text or '',
 +
['marker-symbol'] = marker
 +
}
 +
    })
 +
    center_lat = center_lat + point.latitude
 +
    center_lon = center_lon + point.longitude
 +
    point_count = point_count + 1
 +
end
 +
center_lat = center_lat / point_count
 +
center_lon = center_lon / point_count
 +
 +
local args = {
 +
    ['height'] = width,
 +
    ['width'] = width,
 +
    ['frameless'] = 'frameless',
 +
    ['align'] = 'center',
 +
    ['latitude'] = center_lat,
 +
    ['longitude'] = center_lon
 +
    }
 +
    if default_zoom ~= nil then
 +
    args['zoom'] = default_zoom
 +
    end
 +
return mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), args)
 +
end
 +
 
 +
local function builddynamicmap(map, maptype, width, pointtable, caption, defaultpoint, default_zoom, marker) -- fonction d'aide pour multimap
 +
if map == '-' then
 +
return
 +
end
 +
if map == 'interactive' then
 +
return buildInteractiveMap(width, pointtable, default_zoom, marker)
 +
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 supportées')
 +
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',
 +
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
 
end
  
 
function p.multimap(params)
 
function p.multimap(params)
 
local maplist = params.maplist
 
local maplist = params.maplist
 +
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
 +
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 width = params.width
local latitude = params.latitude
+
local pointtable = params.pointtable
local longitude = params.longitude
+
local caption = params.caption
+
local defaultpoint = params.pointtype
-- les cartes s'affichent en ordre inversé, remettre bien
+
local default_zoom = params.default_zoom
local newlist = {}  
+
local marker = params.marker
for i, j in pairs(maplist) do
+
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
table.insert(newlist, 1, j)
+
pointtable = {{latitude = params.latitude, longitude = params.longitude}}
 +
end
 +
for i, point in ipairs(pointtable) do
 +
point.latitude =  numericcoord(point.latitude)
 +
point.longitude =  numericcoord(point.longitude)
 
end
 
end
maplist = newlist
 
 
 
-- traitement de la largeur
 
-- traitement de la largeur
 
if width and tonumber(width) then
 
if width and tonumber(width) then
Ligne 149 : Ligne 389 :
 
local div =  mw.html.create('div'):addClass('img_toogle')
 
local div =  mw.html.create('div'):addClass('img_toogle')
 
 
if not pointimage then
+
-- met la carte à afficher par défaut à la fin, pour qu'elle soit affichée en premier
pointimage = 'Point carte.svg'
+
if maplist then
 +
table.insert(maplist, maplist[1])
 +
table.remove(maplist, 1)
 
end
 
end
 
 
--transition: appel aux [[Modèle:Géolocalisation/]] n en l'absence de données dans le module
+
--transition: appel aux [[Modèle:Géolocalisation/]] n en l'absence de données dans le module
local mapliststring = table.concat(maplist, '/')
+
for i, j in pairs(maplist or {}) do
for i, j in pairs(maplist) do
+
if j == '' then break end
local success, data = pcall(loaddata, j)  
+
if j ~= 'interactive' then
if not success then
+
local success, data = pcall(loaddata, j)  
return mw.getCurrentFrame():expandTemplate{ title = 'Infobox/Géolocalisation multiple/transition', args = {['géolocalisation'] = mapliststring, type = maptype, latitude = latitude, longitude = longitude }}
+
if not success then
.. '[[Catégorie:Page avec des données de géolocalisation non supportées]]'
+
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 supportées]]'
 +
end
 
end
 
end
 
end
 
end
for i, map in pairs(maplist) do  
+
for i, j in pairs(maplist or {}) do
local newmap = buildmap( map, maptype, width, latitude, longitude, pointimage, caption)
+
if j == '' then break end
 +
local newmap = builddynamicmap( j, maptype, width, pointtable, caption, defaultpoint, default_zoom, marker)
 
div:node(newmap)
 
div:node(newmap)
 +
div:tag('span'):wikitext(maintenance)
 
end
 
end
return div
+
for i, file in pairs(staticmaps or {}) do
end
+
if j == '' then break end
 
+
local caption = "Carte de localisation"
function p.datadoc(frame) -- pour documenter les pages Carte/données/
+
local alt = "Voir la carte détaillée"
local pagename = frame:preprocess('{{PAGENAME}}')
+
local newmap = buildmap( file, caption, alt, width)
local data = require('Module:Carte/données/' .. mw.text.split(pagename , '/')[3])
+
div:node(newmap)
local placename = data.name or ''
+
div:tag('span'):wikitext(maintenance)
-- ajouter ici un script vérifiant que toutes les champs requis sont fournis, et dans un format correct
 
local cat = ''
 
if not string.find(pagename, '/Documentation') then
 
cat = '[[Catégorie:Module de paramétrage de carte (liste complète)|' .. placename .. ']]'
 
 
end
 
end
local introtext = "Cette page contient des données concernant les cartes de géolocalisation pour l'entité suivante : " .. placename
+
return tostring(div)
.. '<br />Voir [[Module:Carte]] pour connaître leur usage.'
 
.. cat
 
local maplist = data.images
 
local gallerytext = ''
 
for i, j in pairs(maplist) do
 
gallerytext = gallerytext  .. '[[File:' .. j .. '|thumb|' .. i .. ']]'
 
end
 
return introtext .. '<br />' .. gallerytext .. cat
 
 
end
 
end
  
Ligne 195 : Ligne 433 :
 
return p.multimap(args)
 
return p.multimap(args)
 
end
 
end
 +
 
return p
 
return p

Version actuelle datée du 23 mai 2017 à 13:08

La documentation pour ce module peut être créée à Module:Carte/doc

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 supporté')
	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]]')
			: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 .. ']]' )
	
	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, marker)
	if marker == nil then
		marker = ''
	end
	-- On fait un hack pour générer une valeur par défaut pour la latitude et la longitude
	local geojson = {
		['type'] = 'FeatureCollection',
		['features'] = {}
	}
	local center_lat = 0
	local center_lon = 0
	local point_count = 0
	for i, point in pairs(pointtable or{}) do -- pour chque point à placer, do
		if not point.pointtype then
			point.pointtype = defaultpoint
		end
		table.insert(geojson['features'], {
			['type'] = 'Feature',
			['geometry'] = {
				['type'] = "Point",
				['coordinates'] = { point.longitude, point.latitude }
			},
			['properties'] = {
				['title'] = point.text or '',
				['marker-symbol'] = marker
			}
    	})
    	center_lat = center_lat + point.latitude
    	center_lon = center_lon + point.longitude
    	point_count = point_count + 1
	end
	center_lat = center_lat / point_count
	center_lon = center_lon / point_count
	
	local args = {
    		['height'] = width,
    		['width'] = width,
    		['frameless'] = 'frameless',
    		['align'] = 'center',
    		['latitude'] = center_lat,
    		['longitude'] = center_lon
    	}
    if default_zoom ~= nil then
    	args['zoom'] = default_zoom
    end
	return mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), args)
end

local function builddynamicmap(map, maptype, width, pointtable, caption, defaultpoint, default_zoom, marker) -- fonction d'aide pour multimap
	if map == '-' then
		return
	end
	if map == 'interactive' then
		return buildInteractiveMap(width, pointtable, default_zoom, marker)
	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 supportées')
	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',
			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
	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
		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 marker = params.marker
	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}}
	end
	for i, point in ipairs(pointtable) do
		point.latitude =  numericcoord(point.latitude)
		point.longitude =  numericcoord(point.longitude)
	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_toogle')
	
	-- 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 supportées]]'
			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, default_zoom, marker)
		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