Which marker groups to show (by default shows the most common groups like see, eat, drink, ...)

data

data=values fills the polygon given by datadata=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

localgetArgs=require('Moduuli:Arguments').getArgslocalp={}functiondbg(v,msg)mw.log((msgor'')..mw.text.jsonEncode(v))end-- Parse all unnamed string parameters in a form of "latitude, longitude" into the real number pairsfunctiongetSequence(args)localcoords={}forind,valinpairs(args)doiftype(ind)=="number"thenlocalvalid=falselocalval2=mw.text.split(val,',',true)-- allow for elevationif#val2>=2and#val2<=3thenlocallat=tonumber(val2[1])locallon=tonumber(val2[2])iflat~=nilandlon~=nilthentable.insert(coords,{lon,lat})valid=trueendendifnotvalidthenerror('Unnamed parameter #'..ind..' "'..val..'" is not recognized as a valid "latitude,longitude" value')endendendreturncoordsend-- 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, 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]]]--functionp.parseGeoSequence(args)localresult=p._parseGeoSequence(args)iftype(result)=='string'thenerror(result)endreturnresultendfunctionp._parseGeoSequence(args)localallTypes={-- how many nested array levels until we get to the Point,-- second is the minimum number of values each Points array must havePoint={1,1},MultiPoint={1,0},LineString={1,2},MultiLineString={2,2},Polygon={2,4},MultiPolygon={3,4},}ifnotallTypes[args.geotype]thenreturn('Unknown geotype '..args.geotype)endlocallevels,min=unpack(allTypes[args.geotype])localresultresult={}fori=1,levelsdoresult[i]={}endlocalgap=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]]localcloseArrays=function(gap)if#result[levels]<minthenerror('Each points array must be at least '..min..' values')elseifmin==1and#result[levels]~=1then-- Pointerror('Point must have exactly one data point')end-- attach arrays in reverse order to the higher order onesfori=levels,levels-gap+1,-1dotable.insert(result[i-1],result[i])result[i]={}endreturn0endlocalusedSequence=falseforvalinmw.text.gsplit(args.data,';',true)dolocalval2=mw.text.split(val,',',true)-- allow for elevationif#val2>=2and#val2<=3andnotusedSequencethenifgap>0thengap=closeArrays(gap)endlocallat=tonumber(val2[1])locallon=tonumber(val2[2])iflat==nilorlon==nilthenreturn('Bad data value "'..val..'"')endtable.insert(result[levels],{lon,lat})elseval=mw.text.trim(val)ifval==''thenusedSequence=falsegap=gap+1if(gap>=levels)thenreturn('Data must not skip more than '..levels-1..' values')endelseifusedSequencethenreturn('Coordinates may not be added right after the named sequence')elseifgap>0thengap=closeArrays(gap)elseif#result[levels]>0thenreturn('Named sequence "'..val..'" cannot be used in the middle of the sequence')end-- Parse value as a sequence name. Eventually we can load data from external data sourcesifval=='values'thenval=getSequence(args)elseifmin==4andval=='world'thenval={{36000,-180},{36000,180},{-36000,180},{-36000,-180},{36000,-180}}elseiftonumber(val)~=nilthenreturn('Not a valid coordinate or a sequence name: '..val)elsereturn('Sequence "'..val..'" is not known. Try "values" or "world" (for Polygons), or specify values as lat,lon;lat,lon;... pairs')endresult[levels]=valusedSequence=trueendendend-- allow one empty last value (some might close the list with an extra semicolon)if(gap>1)thenreturn('Data values must not have blanks at the end')endcloseArrays(levels-1)returnargs.geotype=='Point'andresult[1][1]orresult[1]end-- Run this function to check that the above works okfunctionp.parseGeoSequenceTest()localtestSeq=function(data,expected)localresult=getSequence(data)iftype(result)=='table'thenlocalactual=mw.text.jsonEncode(result)result=actual~=expectedand'data="'..mw.text.jsonEncode(data)..'", actual="'..actual..'", expected="'..expected..'"<br>\n'or''elseresult=result..'<br>\n'endreturnresultendlocaltest=function(geotype,data,expected,values)values=valuesor{}values.geotype=geotype;values.data=data;localresult=p._parseGeoSequence(values)iftype(result)=='table'thenlocalactual=mw.text.jsonEncode(result)result=actual~=expectedand'geotype="'..geotype..'", data="'..data..'", actual="'..actual..'", expected="'..expected..'"<br>\n'or''elseresult='geotype="'..geotype..'", data="'..data..'", error="'..result..'<br>\n'endreturnresultendlocalvalues={' 9 , 8 ','7,6'}localresult=''..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','[[[36000,-180],[36000,180],[-36000,180],[-36000,-180],[36000,-180]],[[36000,-180],[36000,180],[-36000,180],[-36000,-180],[36000,-180]]]')..''returnresult~=''andresultor'Tests passed'endfunctionp._tag(args)localtagname=args.typeor'maplink'iftagname~='maplink'andtagname~='mapframe'thenerror('unknown type "'..tagname..'"')endlocalgeojsonlocaltagArgs={text=args.text,zoom=tonumber(args.zoom),latitude=tonumber(args.latitude),longitude=tonumber(args.longitude),group=args.group,show=args.show,class=args.class,}iftagname=='mapframe'thentagArgs.width=args.width==niland420orargs.widthtagArgs.height=args.height==niland420orargs.heighttagArgs.align=args.align==niland'right'orargs.alignelseifnotargs.classand(args.text==''orargs.text=='""')then-- Hide pushpin icon in front of an empty text linktagArgs.class='no-icon'endifargs.data==''thenargs.data=nilendif(notargs.geotype)~=(notargs.data)then-- one is given, but not the otherifargs.datathenerror('Parameter "data" is given, but "geotype" is not set. Use one of these: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon')elseifargs.geotype=="Point"andtagArgs.latitude~=nilandtagArgs.longitude~=nilthen-- For Point geotype, it is enough to set latitude and logitude, and data will be set up automaticallyargs.data=tagArgs.latitude..','..tagArgs.longitudeelseerror('Parameter data must be set. Use "values" to use all unnamed parameters as coordinates (lat,lon|lat,lon|...), "world" for the whole world, a combination to make a mask, e.g. "world;;values", or direct values "lat,lon;lat,lon..." with ";" as value separator')endend-- Kartographer can now automatically calculate needed zoom & lat/long 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.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.longitudeifargs.imagethenargs.description=(args.descriptionor'')..'[[file:'..args.image..'|300px]]'endifargs.geotypethengeojson={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.parseGeoSequence(args)}}endifargs.debug~=nilthenlocalhtml=mw.html.create(tagname,notgeojsonand{selfClosing=true}ornil):attr(tagArgs)ifgeojsonthenhtml:wikitext(mw.text.jsonEncode(geojson,mw.text.JSON_PRETTY))endreturn'syntaxhighlight',tostring(html)..mw.text.jsonEncode(args,mw.text.JSON_PRETTY),{lang='json'}endreturntagname,geojsonandmw.text.jsonEncode(geojson)or'',tagArgsendfunctionp.tag(frame)localargs=getArgs(frame)localtag,geojson,tagArgs=p._tag(args)returnframe:extensionTag(tag,geojson,tagArgs)endreturnp