Module:Map
Apparence
Ce module fournit des paramètres d'entrée pour les fonctions map
et maplink
prises en charge par Extension:Kartographer.
Utilisation
[modifier]{{#invoke:map|tag|type=|text=|geotype=|title=|latitude=|longitude=|zoom=|marker-symbol=|marker-size=|marker-color=|group=|show=|data=|image=|width=|height=}}
Paramètre | Utilisation | Syntaxe |
---|---|---|
type | maplink ou mapframe selon la fonction qui doit être invoquée
|
|
text | Texte qui apparaîtra dans le coin inférieur gauche de la carte ou en lieu et place des coordonnées | |
geotype | Point pour les points individuels, Polygon pour les polygones
|
|
title | Nom de l'objet | |
latitude et longitude | ||
zoom | Niveau de zoom de la carte | Le niveau par défaut est de « 13 » |
marker-symbol | Symbole, lettre ou numéro à afficher sur la carte comme marqueur | |
marker-size | Taille du symbole, lettre ou numéro à afficher sur la carte comme marqueur |
|
marker-color | Couleur du marqueur de carte |
|
group | Groupe du marqueur (voir, manger, boire, etc.) | |
show | Quels groupes de marqueurs afficher (par défaut, les groupes les plus courants comme voir, manger, boire, etc.) | |
data | data=values remplit le polygone donné par data data=world;;values remplit la zone à l'extérieur du polygone
|
|
image | Nom de l'image affichée dans la vignette | |
width et height | Largeur et hauteur de la carte en px ou % de la largeur de l'écran (uniquement pour mapframe) |
Exemple
[modifier]Avec maplink :
- sans le paramètre « text » : 50°38′50″N 5°35′1″E
- avec le paramètre « text » : Carte de Liège
Avec mapframe :
{{#invoke:map|tag|type=mapframe|text=Carte de [[Liège]]|geotype=Point|title=Musée du Grand Curtius|latitude=50.64709 |longitude=5.58370 |zoom=15|image=LIEGE Palais Curtius - actuel Musée Curtius quai de Maestricht 13 (13-2013).JPG|marker-symbol=museum|marker-size=large|marker-color=4682B4|group=voir}}

Carte de Liège
Voir aussi
[modifier]require('strict')
local getArgs = require('Module:Arguments').getArgs
local p = {}
local insert = table.insert
local concat = table.concat
local split = mw.text.split
local function dbg(v, msg)
mw.log((msg or '') .. mw.text.jsonEncode(v))
end
-- See http://geojson.org/geojson-spec.html
-- Each geotype expects a certain array depth:
-- Point - array of 2 or 3 numbers: [lon, lat] or [lon, lat, elevation]
-- all other types use Point as the basic type
-- MultiPoint - array of 0 or more Points: [Point, ... ]
-- LineString - array of 2 or more Points: [Point, Point, ... ]
-- MultiLineString - array of 1 or more LineStrings: [[Point, Point, ...], ...]
-- Polygon - array of 1 or more LinearRings: [[Point, Point, Point, Point, ... ], ...]
-- each LinearRing is an array of 4 or more Points, where the first and last Points must be the same
-- first LinearRing is the exterior ring, subsequent rings are holes in it
-- MultiPolygon - array of 1 or more Polygons: [[[Point, Point, Point, Point, ...], ...], ...]
local allGeotypes = {
-- how many nested array levels until we get to the Point,
-- second is the minimum number of values each Points array must have
Point = {1, 1},
MultiPoint = {1, 0},
LineString = {1, 2},
MultiLineString = {2, 2},
Polygon = {2, 4},
MultiPolygon = {3, 4},
}
-- Parse all unnamed string parameters in a form of “latitude,longitude” (or “latitude,longitude,elevation”) into the real number pairs
local function getSequence(args)
local coords = {}
for ind, val in pairs(args) do
if type(ind) == 'number' then
local valid = false
local val2 = split(val, ',', true)
if #val2 >= 2 and #val2 <= 3 then -- allow for elevation (ignored here)
local lat = tonumber(val2[1])
local lon = tonumber(val2[2])
if lat ~= nil and lon ~= nil then
insert(coords, {lon, lat})
valid = true
end
end
if not valid then
error('Le paramètre non nommée #' .. ind .. ' « ' .. val .. ' » n’est pas reconnu en tant que valeur « latitude,longitude » valide')
end
end
end
return coords
end
-- Convert a comma and semicolon separated numbers into geojson coordinate arrays
-- For example, for the LineString, data "p1;p2;p3" would be converted to [p1,p2,p3] (each "p" is a [lon,lat] value)
-- LineString has the depth of "1" -- array of points (each point being a two value array)
-- For Polygon, the same sequence "p1;p2;p3" would be converted to [[p1,p2,p3]]
-- Which is an array of array of points. But sometimes we need to specify two subarrays of points:
-- [[p1,p2],[p3]] (last point is in a separate array), and we do it with "p1;p2;;p3"
-- Similarly, for MultiPolygon, "p1;p2;;;p3" would generate [[[p1,p2]],[[p3]]]
function p._parseGeoSequence(args)
if not allGeotypes[args.geotype] then
return 'Géotype « ' .. args.geotype .. ' » inconnu.'
end
local levels, min = unpack(allGeotypes[args.geotype])
local result = {}
-- Example for levels==3, converting “p1 ; p2 ; ; ; p3 ; ; p4” => [[[p1, p2]], [[p3],[p4]]]
-- This function will be called after each gap, and all values are done, so the above will call:
-- before p3: gap=2, [],[],[p1,p2] => [[[p1,p2]]],[],[]
-- before p4: gap=1, [[[p1,p2]]],[],[p3] => [[[p1,p2]]],[[p3]]],[]
-- the end, gap=2, [[[p1,p2]]],[[p3]]],[p4] => [[[p1,p2]],[[p3],[p4]]],[],[]
-- Here, convert at “p1 ; ; ” from [[],[p1]]
local function closeArrays(gap)
if #result[levels] < min then
error('Chaque tableau de points doit contenir au moins ' .. min .. ' points.')
elseif min == 1 and #result[levels] ~= 1 then -- Point
error('Un point doit indiquer exactement un point de données')
end
-- attach arrays in reverse order to the higher order ones
for i = levels, levels - gap + 1, -1 do
insert(result[i - 1], result[i])
result[i] = {}
end
return 0
end
for i = 1, levels do result[i] = {} end
local gap = 0
local usedSequence = false
for val in mw.text.gsplit(args.data, ';', true) do
local val2 = mw.text.split(val, ',', true)
-- allow for elevation
if #val2 >= 2 and #val2 <= 3 and not usedSequence then
if gap > 0 then
gap = closeArrays(gap)
end
local lat = tonumber(val2[1])
local lon = tonumber(val2[2])
if lat == nil or lon == nil then
return 'Valeur de données « ' .. val .. ' » erronée.'
end
insert(result[levels], {lon, lat})
else
val = mw.text.trim(val)
if val == '' then
usedSequence = false
gap = gap + 1
if gap >= levels then
return 'Les données ne doivent pas sauter plus de ' .. (levels - 1) .. ' valeurs.'
end
elseif usedSequence then
return 'Les coordonnées ne doivent pas être ajoutées juste après la séquence nommée.'
else
if gap > 0 then
gap = closeArrays(gap)
elseif #result[levels] > 0 then
return 'La séquence nommée « ' .. val .. ' » ne peut pas être utilisée au milieu de la séquence.'
end
-- Parse value as a sequence name. Eventually we can load data from external data sources
if val == 'values' then
val = getSequence(args)
elseif min == 4 and val == 'world' then
val = {{360, -180}, {360, 180}, {-360, 180}, {-360, -180}, {360, -180}}
elseif tonumber(val) ~= nil then
return 'Pas une coordonnée valide ni un nom de séquence : « ' .. val .. ' ».'
else
return 'La séquence « ' .. val .. '» est inconnue. Essayez « values » ou « world » (pour les polygones) ou spécifiez les valeurs comme une liste de paires « lat,lon;lat,lon;... »'
end
result[levels] = val
usedSequence = true
end
end
end
-- allow one empty last value (some might close the list with an extra semicolon)
if gap > 1 then
return 'Les données ne doivent pas avoir de blancs multiples à la fin'
end
closeArrays(levels-1)
return args.geotype == 'Point' and result[1][1] or result[1]
end
function p.parseGeoSequence(args)
local result = p._parseGeoSequence(args)
if type(result) == 'string' then
error(result)
end
return result
end
-- Run this function to check that the above works ok
function p.parseGeoSequenceTest()
local testSeq = function(data, expected)
local result = getSequence(data)
if type(result) == 'table' then
result = mw.text.jsonEncode(result)
end
return result == expected and '' or
'Erreur :\n données = « ' .. mw.text.jsonEncode(data) .. ' »,\n résultat = « ' .. result .. ' »,\n attendu = « ' .. expected .. ' ».'
end
local test = function(geotype, data, expected, values)
values = values or {}
values.geotype = geotype
values.data = data
local result = p._parseGeoSequence(values)
if type(result) == 'table' then
result = mw.text.jsonEncode(result)
end
return result == expected and '' or
'Erreur :\n geotype = « ' .. geotype .. ' », données = « ' .. mw.text.jsonEncode(data) .. ' »,\n résultat = « ' .. result .. ' »,\n attendu = « ' .. expected .. ' ».'
end
local values = {' 9 , 8 ', '7,6'}
local result = ''
.. testSeq({}, '[]')
.. testSeq({'\t\n 1 \r,-10'}, '[[-10,1]]')
.. testSeq(values, '[[8,9],[6,7]]')
.. test('Point', '1,2', '[2,1]')
.. test('MultiPoint', '1,2;3,4;5,6', '[[2,1],[4,3],[6,5]]')
.. test('LineString', '1,2;3,4', '[[2,1],[4,3]]')
.. test('MultiLineString', '1,2;3,4', '[[[2,1],[4,3]]]')
.. test('MultiLineString', '1,2;3,4;;5,6;7,8', '[[[2,1],[4,3]],[[6,5],[8,7]]]')
.. test('Polygon', '1,2;3,4;5,6;1,2', '[[[2,1],[4,3],[6,5],[2,1]]]')
.. test('MultiPolygon', '1,2;3,4;5,6;1,2', '[[[[2,1],[4,3],[6,5],[2,1]]]]')
.. test('MultiPolygon', '1,2;3,4;5,6;1,2;;11,12;13,14;15,16;11,12', '[[[[2,1],[4,3],[6,5],[2,1]],[[12,11],[14,13],[16,15],[12,11]]]]')
.. test('MultiPolygon', '1,2;3,4;5,6;1,2;;;11,12;13,14;15,16;11,12', '[[[[2,1],[4,3],[6,5],[2,1]]],[[[12,11],[14,13],[16,15],[12,11]]]]')
.. test('MultiPolygon', '1,2;3,4;5,6;1,2;;;11,12;13,14;15,16;11,12;;21,22;23,24;25,26;21,22', '[[[[2,1],[4,3],[6,5],[2,1]]],[[[12,11],[14,13],[16,15],[12,11]],[[22,21],[24,23],[26,25],[22,21]]]]')
.. test('MultiLineString', 'values;;1,2;3,4', '[[[8,9],[6,7]],[[2,1],[4,3]]]', values)
.. test('Polygon', 'world;;world', '[[[360,-180],[360,180],[-360,180],[-360,-180],[360,-180]],[[360,-180],[360,180],[-360,180],[-360,-180],[360,-180]]]')
return result ~= '' and result or 'Tests réussis'
end
function p._tag(args)
local tagname = args.type or 'maplink'
if tagname ~= 'maplink' and tagname ~= 'mapframe' then
error('Type de balise « ' .. tagname .. ' » inconnu (essayez « maplink » ou « mapframe »)')
end
local tagArgs = {
text = args.text,
zoom = tonumber(args.zoom),
latitude = tonumber(args.latitude),
longitude = tonumber(args.longitude),
group = args.group,
show = args.show,
class = args.class,
url = args.url,
alt = args.alt,
image = args.image,
}
if args.wikidata ~= nil then
local e = mw.wikibase.getEntity(args.wikidata)
if e.claims ~= nil then
if (not tagArgs.latitude or not tagArgs.longitude) then
if e.claims.P625 ~= nil then
tagArgs.latitude = e.claims.P625[1].mainsnak.datavalue.value.latitude
tagArgs.longitude = e.claims.P625[1].mainsnak.datavalue.value.longitude
end
end
if not args.title then
-- always try to fetch title, to get a reference in 'Wikidata entities used in this page'
if e.labels.fr ~= nil then
args.title = e.labels.fr.value
elseif e.labels.en ~= nil then
args.title = e.labels.en.value
end
end
--if not tagArgs.url then
-- if e.claims.P856 ~= nil then
-- tagArgs.url = e.claims.P856[1].mainsnak.datavalue.value
-- end
--end
if not tagArgs.image then
if e.claims.P18 ~= nil then
tagArgs.image = e.claims.P18[1].mainsnak.datavalue.value
end
end
end
end
args.title = args.title or ''
tagArgs.title = args.title
tagArgs.alt = tagArgs.alt or ''
tagArgs.url = tagArgs.url or ''
tagArgs.image = tagArgs.image or ''
if tagArgs.image ~= '' then
args.description = '[[File:' .. tagArgs.image .. '|300px|]]'
.. (args.description and '<br /> <small>' .. args.description .. '</small>' or '')
end
if args.ismarker and (args.latitude == 'NA' or args.longitude == 'NA' or not tagArgs.latitude or not tagArgs.longitude) then
return 'nowiki', '', tagArgs
end
if tagname == 'mapframe' then
tagArgs.width = args.width or 420
tagArgs.height = args.height or 420
tagArgs.align = args.align or 'right'
elseif not args.class and (args.text == '' or args.text == '""') then
tagArgs.class = 'no-icon' -- Hide pushpin icon in front of an empty text link
end
if args.data == '' then
args.data = nil
end
if (not args.geotype) ~= (not args.data) then
-- one is given, but not the other
if args.data then
error('Le paramètre « data » est fourni, mais « geotype » ne l’est pas (utilisez un de ces types : Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon)')
elseif args.geotype == 'Point' and tagArgs.latitude ~= nil and tagArgs.longitude ~= nil then
-- For Point geotype, it is enough to set latitude and logitude, and data will be set up automatically
args.data = tagArgs.latitude .. ',' .. tagArgs.longitude
else
error('Le paramètre « data » doit être défini. Utiliser « values » pour utiliser tous les paramètres non nommés en tant que coordonnées (lat,lon|lat,lon|...), « world » pour le monde entier, une combinaison comme « world;values » pour créer un masque, ou bien directement des coordonnées « lat,lon;lat,lon... » avec « ; » comme séparateur de points')
end
end
local geojson
-- Kartographer can now automatically calculate the needed latitude/longitude/zoom, based on the data provided.
-- Current version ignores mapmasks, but that will also be fixed soon. Leaving this for now, but can be removed if all is good.
--tagArgs.latitude = tagArgs.latitude or 0
--tagArgs.longitude = tagArgs.longitude or 0
--tagArgs.zoom = tagArgs.zoom or 14
if args.geotype then
geojson = {
type = 'Feature',
properties = {
title = args.title,
description = args.description,
alt = args.alt,
['marker-size'] = args['marker-size'],
['marker-symbol'] = args['marker-symbol'],
['marker-color'] = args['marker-color'],
stroke = args.stroke,
['stroke-opacity'] = tonumber(args['stroke-opacity']),
['stroke-width'] = tonumber(args['stroke-width']),
fill = args.fill,
['fill-opacity'] = tonumber(args['fill-opacity']),
},
geometry = {
type = args.geotype,
coordinates = p.parseGeoSequence(args)
}
}
end
if args.debug ~= nil then
local html = mw.html.create(tagname, not geojson and {selfClosing=true} or nil):attr(tagArgs)
if geojson then
html:wikitext(mw.text.jsonEncode(geojson, mw.text.JSON_PRETTY))
end
return 'syntaxhighlight', tostring(html) .. mw.text.jsonEncode(args, mw.text.JSON_PRETTY),
{lang = 'json', latitude = 0, longitude = 0, title = '', url = ''}
end
return tagname, geojson and mw.text.jsonEncode(geojson) or '', tagArgs
end
local listingMarkerTypes = {'see', 'eat', 'buy', 'drink', 'sleep'}
local regionMarkerTypes = {'city', 'vicinity'}
function p.tag(frame)
local args = getArgs(frame)
local tag, geojson, tagArgs = p._tag(args)
local out = {}
local function has_value(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
if args.ismarker == 'yes' then
if mw.title.getCurrentTitle().namespace == 0
and has_value({'do', unpack(listingMarkerTypes)}, string.lower(args.group)) -- prepend to copy of listingMarkerTypes
then
insert(out, '[[Category:Has ' .. string.lower(args.group) .. ' listing]]')
end
if geojson ~= '' then
-- local coordargs = { tagArgs.latitude, tagArgs.longitude, ['title'] = tagArgs.title }
insert(out, '<span class="noprint listing-coordinates" style="display:none">'
.. '<span class="geo">'
.. '<abbr class="latitude">' .. tagArgs.latitude .. '</abbr>'
.. '<abbr class="longitude">' .. tagArgs.longitude .. '</abbr>'
.. '</span>'
.. '</span>'
.. '<span title="Carte pour le marqueur « '.. args.group ..' »">' -- TODO
.. frame:extensionTag(tag, geojson, tagArgs)
.. ' </span>')
if mw.title.getCurrentTitle().namespace == 0 then
insert(out, '[[Category:Article avec marqueur]]')
end
else
if mw.title.getCurrentTitle().namespace == 0 and has_value(listingMarkerTypes, string.lower(args.group))
and (args.latitude ~= 'NA' and args.longitude ~= 'NA') then
insert(out, '[[Category:' .. string.lower(args.group).. ' listing with no coordinates]]')
end
end
if mw.title.getCurrentTitle().namespace == 0 and has_value(regionMarkerTypes, string.lower(args.group))
then
if args.wikidata == nil or args.wikidata == '' then
insert(out, '[[Category:Region markers without wikidata]]')
end
if args.image == nil or args.image == '' then
insert(out, '[[Category:Region markers without image]]')
end
end
if tagArgs.title ~= '' then
--title = '<b id="' .. mw.uri.anchorEncode(tagArgs.title) .. '" class="fn org listing-name">' .. tagArgs.title .. '</b>'
insert(out, '<b class="fn org listing-name">' .. tagArgs.title .. '</b>')
end
if tagArgs.alt ~= '' then
insert(out, ' <em>(' .. tagArgs.alt .. ')</em>')
end
return concat(out, '')
else
return frame:extensionTag(tag, geojson, tagArgs)
end
end
return p