Module:Carte : Différence entre versions

De Lagny-sur-Marne Wiki
Aller à : navigation, rechercher
(pas "équidistant", "linéaire" en fait mais ça ne marche pas pour l'instant, remplace par l'utilisation des données "x" et "y" comme dans le module anglais)
(passe toutes les coordonnées en format numérique dès le début pour compatibilité avec les cartes intéractives (Utilisateur:Zolo/test))
 
(61 révisions intermédiaires par 8 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
 
local p = {}
 
local p = {}
local mapdatamod = require('Module:Données cartes')
 
 
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 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'   
 
-- '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 18 : 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 30 : 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
 
end
local mapdata =  mapdatamod.main(map)
 
if not mapdata or not mapdata.images then
 
addmaintenancecat('Page avec des données de géolocalisation non supportées')
 
return "carte non supportée: " .. map
 
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, '$1', tostring(latitude))
 
ypos = string.gsub(ypos, '$2', tostring(longitude))
 
ypos = tonumber(mw.ext.ParserFunctions.expr(ypos))
 
xpos = string.gsub(xpos, '$1', tostring(latitude))
 
xpos = string.gsub(xpos, '$2', 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 92 : 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 144 : 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
if not require('Module:Données cartes').main(j) then
+
if j ~= 'interactive' then
return
+
local success, data = pcall(loaddata, j)
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
 +
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
 
end
  
Ligne 170 : 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