Module:Carte
Wikimedia module / De Wikipedia, l'encyclopédie encyclopedia
local p = {} local pointmod = require('Module:Carte/Points') local linguistic = require('Module:Linguistique') local maintenance = local coord = require('Module:Coordinates') local wd = require('Module:Wikidata') 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 .. '' 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 '' .. 'les coordonnées indiquées sont hors de la carte de géolocalisation demandée' end
local pointsize = tostring(point.pointsize or '8') local pointtext = point.text or local pointtype = point.pointtype or 'default' local pointimage = pointmod[pointtype] if not pointimage then pointimage = pointmod.default addmaintenancecat('Page avec un modèle de point de carte non pris en charge') end
local htmlheight = tostring(ypos * 100) .. '%' local htmlwidth = tostring(xpos * 100) .. '%'
local pointdiv = mw.html.create('div') :css{position = 'absolute', border = 'none', top = htmlheight, left = htmlwidth} :tag('div') :css{position = 'absolute', top = '-4px', left = '-4px', ['line-height'] = '0', width = '8px'} :wikitext('Fichier:' .. pointimage .. '') :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('Fichier:' .. file .. '' )
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 maxpoints(points) if not points then return nil end local minlat, maxlat, minlong, maxlong = points[1].latitude, points[1].latitude, points[1].longitude, points[1].longitude for i, point in ipairs(points) do minlat = math.min(point.latitude, minlat) maxlat = math.max(point.latitude, maxlat) minlong = math.min(point.longitude, minlong) maxlong = math.max(point.longitude, maxlong) end return minlat, maxlat, minlong, maxlong end
local function guesszoom(ids) if (not ids) then return nil end local item = ids[1] local area = wd.formatStatements{entity = item, property = "P2046", targetunit = "square kilometer", displayformat = "raw"} if (not area) or not(tonumber(area)) then return nil end area = tonumber(area) if area > 100000 then return 3 end if area > 10000 then return 4 end if area > 1000 then return 5 end if area > 100 then return 6 end return 7 end
local function guesszoom2(minlat, maxlat, minlong, maxlong) if not (minlat and maxlat and minlong and maxlong) or ((minlat == maxlat) and (minlong == maxlong) ) then return nil end local x = coord._distance({latitude = (maxlat + minlat / 2), longitude = minlong}, {latitude = (maxlat + minlat / 2), longitude = maxlong }) local y = coord._distance({latitude = 0, longitude = minlong}, {latitude = 0, longitude = maxlong}) local dist = math.max(x, y) -- à ajuster si la carte n'est pas carrée if (dist > 512) then return 4 elseif (dist > 256) then return 5 elseif (dist > 128) then return 6 elseif (dist> 64) then return 7 elseif (dist > 32) then return 8 elseif (dist > 16) then return 9 elseif (dist > 8) then return 10 elseif (dist > 4) then return 11 elseif (dist > 2) then return 12 end return 13 end
local function buildInteractiveMap(width, pointtable, default_zoom, ids, shapecolor) -- On fait un hack pour générer une valeur par défaut pour la latitude et la longitude local geojson = {}
shapecolor = shapecolor or '#800000'
for i, point in pairs(pointtable or{}) do -- pour chque point à placer, do
table.insert(geojson, {
['type'] = 'Feature',
['geometry'] = {
['type'] = "Point",
['coordinates'] = { point.longitude, point.latitude }
},
['properties'] = {
['title'] = point.text or ,
['marker-symbol'] = point.marker,
['marker-color'] = point.markercolor or "#224422",
}
})
if ids then local geojson2 = { ['type'] = 'ExternalData',
['service'] = 'geoshape', ['ids'] = ids, properties = { ['fill'] = shapecolor or '#800000', },
} table.insert(geojson, geojson2) end
end
local minlat, maxlat, minlong, maxlong = maxpoints(pointtable) local center_lat = (maxlat + minlat) / 2 local center_long = (maxlong + minlong) / 2 -- 180e méridien if (maxlong - minlong) > 180 then centerlong = centerlong - 180 end local args = {
['height'] = width, ['width'] = width, ['frameless'] = 'frameless', ['align'] = 'center', ['latitude'] = center_lat, ['longitude'] = center_long, ['zoom'] = default_zoom or guesszoom(ids) or guesszoom2(minlat, maxlat, minlong, maxlong) or 13 }
return mw.getCurrentFrame():extensionTag('mapframe', mw.text.jsonEncode(geojson), args) end
local function builddynamicmap(map, maptype, width, pointtable, caption, defaultpoint, globe, default_zoom, ids, shapecolor) -- fonction d'aide pour multimap if map == '-' then return end if map == 'interactive' then if (globe and (globe ~= 'earth')) then return nil end return buildInteractiveMap(width, pointtable, default_zoom, ids, shapecolor) end
local success, mapdata = pcall(loaddata, map) if not success or not mapdata.images then addmaintenancecat('Page avec des données de géolocalisation non prises en charge') end local name = mapdata.name or '?'
-- analyse linguistique pour le texte de la carte local datagender = mapdata.genre or local gender = string.sub(datagender, 1, 1) -- ms = masculin-singulier, fp = féminin pluriel etc. local number = string.sub(datagender, 2, 2) local determiner = mapdata.determiner local ofstring = linguistic.of(name, gender, number, determiner) -- restitue "de France" ou "du Japon"
local mapname = mapdata.name local file = mapdata.images[maptype] or mapdata.images['default'] or mapdata.images[1] if not file then file = mapdata.images['default'] end local alt = 'voir sur la carte ' .. ofstring local caption = 'Localisation sur la carte ' .. ofstring return buildmap(file, caption, alt, width, mapdata, pointtable, defaultpoint) end
local function guessmaps(params) -- cas non terriens local globe = params.globe if globe and (globe ~= 'earth') then local maps = { moon = 'Lune', mars = 'Mars', mercury = 'Mercure', neptune = 'Neptune', venus = 'Vénus', callisto = 'Callisto', ceres = 'Cérès', charon = 'Charon', enceladus = 'Encelade', europa = 'Europe', io = 'Io', iapetus = 'Japet', ganymede = 'Ganymède', pluto = 'Pluton', tethys = 'Téthys', titan = 'Titan', triton = 'Triton', vesta = 'Vesta', } return maps[mw.ustring.lower(globe)] end
-- autres cas
local data = require('Module:Carte/données') local validmaps = {} local lat = tonumber(params.latitude) or tonumber(coord.dms2dec({args={params.latitude}})) local long = tonumber(params.longitude) or tonumber(coord.dms2dec({args={params.longitude}})) if not lat or not long then return nil end for i, map in pairs(data) do if lat < map.top and lat > map.bottom then if map.left > map.right then -- correction pour les cartes passant le méridien 180 if long < 0 then map.left = map.left - 360 else map.right = 360 + map.right end end if (long > map.left and long < map.right) then table.insert(validmaps, map) end end end if #validmaps == 0 then return nil end
local function area(map) -- fonction simple juste pour pouvoir classer apprximativement les cartes pas superficie return (math.abs(map.top - map.bottom)) * (math.abs(map.left- map.right)) end
table.sort(validmaps, function(a, b) return area(a) < area(b) end)
local chosenmaps = {} -- on ne les gardes pas toutes, ça serait souvent trop
local havezone = {} -- paramètre "zone" des carte déjà obtenu, pour ne pas avoir le même deux fois: { zone = {nom de la carte, position de la carte} }
local forbiddenzones = { -- zone peu utiles, à ne pas utiliser par défaut ['future région française'] = true, ['france'] = true, -- utilisé pour des zone non administratives, généalement pas pratique ['italie'] = true, }
local function addmap(map, pos) -- ajoute la carte à la liste et enregistre qu'elle a une carte avec ce paramètre "zone" if pos then chosenmaps[pos] = map.name havezone[map.zone] = {map, pos} else table.insert(chosenmaps, map.name) if map.zone then havezone[map.zone] = {map, #chosenmaps} end end end
local function centrality(map) -- retourne un indice ah hoc de centralité, plus faible si le point est près d'un bord local function compute(point, end1, end2) local pct = (point - end1) / (end2- end1) return 0.5 - math.abs(0.5 - pct) end local latcentrality = compute(lat, map.top, map.bottom) local longcentrality = compute(long, map.left, map.right) return math.min(latcentrality, longcentrality) end
for i, map in pairs(validmaps) do if not(havezone[map.zone]) and (not forbiddenzones[map.zone]) and #chosenmaps < 3 then addmap(map) end if map.zone and havezone[map.zone] and (centrality(map) > centrality(havezone[map.zone][1] )) then -- si deux cartes ont le même paramètre "zone", on prend la mieux centrée addmap(map, havezone[map.zone][2]) end end addmaintenancecat('Carte de localisation ajoutée par Module:Carte') return chosenmaps end
function p.multimap(params) local maplist = params.maplist local globe = params.globe if not maplist and params.guessmaps ~= '-' then -- guessmaps pourrait prendre d'autres paramètres (échelle, etc.) maplist = guessmaps(params) end if type(maplist) == 'string' then if maplist == 'non' or maplist == 'pas pertinent' or maplist == 'non' then return elseif maplist == 'interactive' and globe and globe ~= 'earth' then maplist = guessmaps(params) end maplist = mw.text.split(maplist, '/', true) end local staticmaps = params.staticmaps if type(staticmaps == 'string') then staticmaps = {staticmaps} end
if (not maplist) and (not staticmaps) then return nil end local maptype = params.maptype -- à retravailler pour quand on veut la même région, mais avec plusieurs types de carte local width = params.width local pointtable = params.pointtable local caption = params.caption local defaultpoint = params.pointtype local default_zoom = params.default_zoom local ids = params.ids -- wikidata ids if not pointtable then -- pointtable est la liste des points à placer, mais lorsqu'il n'y a qu'un seul, on peut simplement avoir latitude et longitude pointtable = Modèle:Latitude = params.latitude, longitude = params.longitude, marker = params.marker, markercolor = params.markercolor end for i=#pointtable, 1, -1 do local point = pointtable[i] point.latitude = numericcoord(point.latitude) point.longitude = numericcoord(point.longitude) if not ( point.latitude and point.longitude ) then table.remove( pointtable, i ) end point.markercolor = point.markercolor or params.markercolor point.marker = point.marker or params.marker end if #pointtable == 0 then return end -- traitement de la largeur if width and tonumber(width) then width = tonumber(width) else width = 280 end-- si pas un nombre, erreur ? local div = mw.html.create('div'):addClass('img_toggle')
--transition: appel aux Modèle:Géolocalisation/ n en l'absence de données dans le module
for i, j in ipairs(maplist or {}) do if j == then break end if j ~= 'interactive' then local success, data = pcall(loaddata, j) if not success then local mapliststring = table.concat(maplist, '/') return mw.getCurrentFrame():expandTemplate{title = 'Infobox/Géolocalisation multiple/transition', args = {['géolocalisation'] = mapliststring, type = maptype, latitude = params.latitude or pointtable[1].latitude, longitude = params.longitude or pointtable[1].longitude}} .. '' end end end for i = #maplist, 1, -1 do if maplist[i] ~= then local newmap = builddynamicmap( maplist[i], maptype, width, pointtable, caption, defaultpoint, globe, default_zoom, ids, params.shapecolor) div:node(newmap) div:tag('span'):wikitext(maintenance) end end for i, file in pairs(staticmaps or {}) do if j == then break end local caption = "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