Module:Carte : Différence entre versions
(passe toutes les coordonnées en format numérique dès le début pour compatibilité avec les cartes intéractives (Utilisateur:Zolo/test)) |
|||
(73 révisions intermédiaires par 8 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
local p = {} | local p = {} | ||
− | |||
local pointmod = require('Module:Carte/Points') | local pointmod = require('Module:Carte/Points') | ||
− | local linguistic = require('Module: | + | 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', | ||
+ | } | ||
− | function | + | local function addmaintenancecat(cat, sortkey) -- ajoute du texte à la string maintenance en cas de problème |
− | if | + | if mw.title.getCurrentTitle().namespace ~= 0 then |
return | return | ||
end | end | ||
− | local mapdata = | + | maintenance = maintenance .. '[[Category:' .. cat .. ']]' |
− | + | end | |
− | + | ||
− | return | + | -- '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 | end | ||
− | |||
− | if (not | + | 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 | 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' | |
− | if | ||
− | |||
− | |||
− | |||
− | return | ||
end | end | ||
− | + | ||
− | addmaintenancecat(' | + | 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( | + | |
− | local htmlwidth = tostring( | + | local htmlheight = tostring(ypos * 100) .. '%' |
+ | local htmlwidth = tostring(xpos * 100) .. '%' | ||
local pointdiv = mw.html.create('div') | local pointdiv = mw.html.create('div') | ||
:css{position = 'absolute', border = 'none', top = htmlheight, left = htmlwidth} | :css{position = 'absolute', border = 'none', top = htmlheight, left = htmlwidth} | ||
:tag('div') | :tag('div') | ||
− | :css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0'} | + | :css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0', width = '8px'} |
− | :wikitext('[[Image:' .. pointimage .. '| | + | :wikitext('[[Image:' .. pointimage .. '|' .. pointsize .. 'px]]') |
− | + | :tag('span') | |
− | + | :css{position = 'absolute', ['text-align'] = 'left', width = '150px'} | |
− | + | :wikitext(pointtext) | |
− | + | :done() | |
− | + | :allDone() | |
− | + | ||
− | + | return pointdiv | |
− | end | + | end |
− | + | ||
+ | local function buildmap(file, caption, alt, width, mapdata, pointtable, defaultpoint) | ||
local map = mw.html.create('div') | local map = mw.html.create('div') | ||
− | :css | + | :css(divstyle) |
:addClass("geobox") | :addClass("geobox") | ||
− | :wikitext( | + | :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('[[ | + | :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 |
− | 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 | ||
+ | |||
+ | 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 | 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