Module:Map: Difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
No edit summary |
No edit summary |
||
Line 161: | Line 161: | ||
if type == 'maplink' or type == 'mapframe' then |
if type == 'maplink' or type == 'mapframe' then |
||
tagArgs.zoom = tagArgs.zoom == nil and 14 or tagArgs.zoom |
tagArgs.zoom = tagArgs.zoom == nil and 14 or tagArgs.zoom |
||
tagArgs.latitude = tagArgs.latitude == nil and 51.47766 or tagArgs.latitude |
tagArgs.latitude = tagArgs.latitude == nil and 51.47766 or tagArgs.latitude |
||
tagArgs.longitude = tagArgs.longitude == nil and -0.00115 or tagArgs.longitude |
tagArgs.longitude = tagArgs.longitude == nil and -0.00115 or tagArgs.longitude |
||
if type == 'mapframe' then |
if type == 'mapframe' then |
||
Line 204: | Line 204: | ||
type = 'syntaxhighlight' |
type = 'syntaxhighlight' |
||
geojson = { |
geojson = { |
||
params = args, |
|||
tagArgs = tagArgs, |
|||
geojson = geojson, |
geojson = geojson, |
||
} |
} |
Revision as of 03:59, 10 June 2016
This documentation is transcluded from Module:Map/doc. (edit | history)
Note to editors: Please don't categorize this template by editing it directly. Instead, place the category in its documentation page, in its "includeonly" section.
Note to editors: Please don't categorize this template by editing it directly. Instead, place the category in its documentation page, in its "includeonly" section.
This module provides input parameters for mapframe and maplink functions supported by Extension:Kartographer.
Usage:
{{#invoke:map|tag|type=maplink|geotype=Point|title=Example|latitude=59.0|longitude=29.0}}
Parameter | Usage |
---|---|
type | maplink oder mapframe depending on which function should be invoked
|
geotype | Point for individual points, Polygon for polygons
|
title | Object name |
latitude and longitude | use 'NA' to disable coordinates, including the ones from wikidata |
zoom | Zoom level of the map |
marker-symbol | Symbol, letter, or number for displaying on the map as marker |
marker-color | Color of the map marker |
group | Group of markers (see, eat, drink, etc.) |
show | Which marker groups to show (by default shows the most common groups like see, eat, drink, ...) |
data | data=values fills the polygon given by data data=world;;values fills the area outside of the polygon
|
image | Name of the image shown in the thumbnail |
width and height | map width and map height in px or % of screen width, only for mapframe |
wikidata | if specified, the missing title/lat/long/image fields will be fetched from the respective wikidata entry fields |
local getArgs = require('Module:Arguments').getArgs
local p = {}
function extractCoordinates(args)
local coords = {}
local namedCoords = { unnamed = coords }
-- Parse an array of strings of number pairs "latitude, longitude" into the real number pairs
for k, v in pairs( args ) do
if type(k) == "number" then
local v2 = mw.text.split( v, ',', true )
if #v2 == 2 then
local lat = tonumber(v2[1])
local lon = tonumber(v2[2])
if lat ~= nil and lon ~= nil then
table.insert(coords, { lon, lat } )
end
end
end
end
return namedCoords
end
-- See http://geojson.org/geojson-spec.html
-- Convert a comma and semicolon separated numbers into geojson coordinate arrays
-- Each geotype expects a certain array depth:
-- Point - [ lon, lat ] All other types use point as the basic type
-- MultiPoint - array of points: [ point, ... ]
-- LineString - array of 2 or more points: [ point, point, ... ]
-- MultiLineString - array of LineStrings: [ [ point, point, ... ], ... ]
-- Polygon - [ [ point, point, point, point, ... ], ... ]
-- each LinearRing is an array of 4 or more points, where first and last must be the same
-- first LinearRing is the exterior ring, subsequent rings are holes in it
-- MultiPolygon - array of Polygons: [ [ [ point, point, point, point, ... ], ... ], ... ]
--
-- For example, for the LineString, value "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.parseCoordinateParam(geotype, value, namedCoords)
local allTypes = {
-- 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 },
}
if not allTypes[geotype] then error('Unknown geotype ' .. geotype) end
local levels, min = unpack(allTypes[geotype])
if min == 4 then
-- Cover the whole world
namedCoords.world = {{36000,-180}, {36000,180}, {-36000,180}, {-36000,-180}, {36000,-180}}
end
local result
result = {}
for i = 1, levels do result[i] = {} end
local gap = 0
-- 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 closeArrays = function (gap)
if #result[levels] < min then
error('Each points array must be at least ' .. min .. ' values')
elseif min == 1 and #result[levels] ~= 1 then
-- Point
error('Point must have exactly one data point')
elseif min == 4 then
-- Polygon or MultiPolygon
local lon1, lat1 = unpack(result[levels][1])
local lon2, lat2 = unpack(result[levels][#result[levels]])
if (lon1 ~= lon2 or lat1 ~= lat2) then
error("Polygon's first and last points must be the same")
end
end
-- attach arrays in reverse order to the higher order ones
for i = levels, levels-gap+1, -1 do
table.insert(result[i-1], result[i])
result[i] = {}
end
end
if value ~= '' then
local namedRangeUsed = false
for v in mw.text.gsplit(value, ';', true) do
local v2 = mw.text.split( v, ',', true )
if #v2 == 2 and not namedRangeUsed then
if gap > 0 then
closeArrays(gap)
gap = 0
end
local lat = tonumber(v2[1])
local lon = tonumber(v2[2])
if lat == nil or lon == nil then error('Bad data value "' .. v .. '"') end
table.insert(result[levels], { lon, lat } )
else
v = mw.text.trim(v)
if v == '' then
namedRangeUsed = false
gap = gap + 1
if (gap >= levels) then error('Data must not skip more than ' .. levels-1 .. ' values') end
elseif namedRangeUsed then
error('Coordinates may not be added after named range was used')
elseif tonumber(v) ~= nil then
error('Not a valid coordinate: ' .. v)
elseif #result[levels] > 0 then
error('Named range "' .. v .. '" cannot be used in the middle of the sequence')
elseif not namedCoords[v] then
error('Named range "' .. v .. '" is not known. Try "unnamed", "world"')
else
result[levels] = namedCoords[v]
end
end
end
if (gap > 0) then error('Data values must not have blanks at the end') end
end
closeArrays(levels-1)
return geotype == 'Point' and result[1][1] or result[1]
end
-- Run this function to check that the above works ok
function p._debug_parseCoordinateParam()
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiPoint', '')) == '[]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('Point', '1,2')) == '[2,1]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiPoint', '1,2;3,4;5,6')) == '[[2,1],[4,3],[6,5]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('LineString', '1,2;3,4')) == '[[2,1],[4,3]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiLineString', '1,2;3,4')) == '[[[2,1],[4,3]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiLineString', '1,2;3,4;;5,6;7,8')) == '[[[2,1],[4,3]],[[6,5],[8,7]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiPolygon', '1,2;3,4;5,6;1,2')) == '[[[[2,1],[4,3],[6,5],[2,1]]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('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]]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('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]]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('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]]]]')
assert(mw.text.jsonEncode(p.parseCoordinateParam('MultiLineString', 'aa;;1,2;3,4',{aa={{9,8},{7,6}}}) == '[[[9,8],[7,6]],[[2,1],[4,3]]]'))
end
function p._tag(args)
local type = args.type or 'maplink'
local namedCoords = extractCoordinates(args)
local tagArgs = {
text = args.text,
zoom = tonumber(args.zoom),
latitude = tonumber(args.latitude),
longitude = tonumber(args.longitude),
group = args.group,
show = args.show,
}
local geojson
if type == 'maplink' or type == 'mapframe' then
tagArgs.zoom = tagArgs.zoom == nil and 14 or tagArgs.zoom
tagArgs.latitude = tagArgs.latitude == nil and 51.47766 or tagArgs.latitude
tagArgs.longitude = tagArgs.longitude == nil and -0.00115 or tagArgs.longitude
if type == 'mapframe' then
tagArgs.width = tagArgs.width == nil and 420 or tagArgs.width
tagArgs.height = tagArgs.height == nil and 420 or tagArgs.height
end
else
error('unknown type')
end
if args.geotype then
if args.geotype == "Point" and #namedCoords.unnamed == 0 and not args.data then
if tagArgs.latitude ~= nil and tagArgs.longitude ~= nil then
args.data = { tagArgs.latitude, tagArgs.longitude }
end
end
geojson = {
type = "Feature",
properties = {
title = args.title,
description = args.description,
['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.parseCoordinateParam(args.geotype, args.data, namedCoords )
}
}
end
-- Debug support -- simply supply a debug field for a pretty print
local flags
if args.debug ~= nil then
type = 'syntaxhighlight'
geojson = {
params = args,
tagArgs = tagArgs,
geojson = geojson,
}
tagArgs = { lang = 'json' }
flags = mw.text.JSON_PRETTY
end
return type, geojson and mw.text.jsonEncode(geojson, flags) or '', tagArgs
end
function p.tag(frame)
local args = getArgs(frame, { parentFirst = true })
local tag, geojson, tagArgs = p._tag(args)
return frame:extensionTag( tag, geojson, tagArgs)
end
return p