Module:Location map
Tools
Actions
General
Wikimedia Projects
Print/export
In other projects
| This module issubject to page protection. It is ahighly visible module in use by a very large number of pages, or issubstituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it isprotected from editing. |
| This Lua module is used on545,000+ pages, or roughly 227% of all pages. To avoid major disruption and server load, any changes should be tested in the module's/sandbox or/testcases subpages, or in your ownmodule sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on thetalk page before implementing them. |
| This module employs intricate features of template syntax. You are encouraged to familiarise yourself with its setup andparser functions before editing the template. If your edit causes unexpected problems, pleaseundo it quickly, as this template may appear on a large number of pages. You can conduct experiments, and should test all major changes, in eitherthis template's sandbox, thegeneral template sandbox, or your user space before changing anything here. |
This module implements the {{Location map}} and {{Location map~}} templates. Please see the template pages for usage instructions.
Since the introduction of support for different captions when multiple maps are utilized, an issue has been highlighted.
Some infobox templates use their caption parameter directly instead of passing it to this module. This results in the display of a "##" between the two captions.
If you are unable to edit the infobox template, contactBellezzasolo.
require('strict')localp={}localgetArgs=require('Module:Arguments').getArgslocalfunctionround(n,decimals)localpow=10^(decimalsor0)returnmath.floor(n*pow+0.5)/powendfunctionp.getMapParams(map,frame)ifnotmapthenerror('The name of the location map definition to use must be specified',2)endlocalmoduletitle=mw.title.new('Module:Location map/data/'..map)ifnotmoduletitlethenerror(string.format('%q is not a valid name for a location map definition',map),2)elseifmoduletitle.existsthenlocalmapData=mw.loadData('Module:Location map/data/'..map)returnfunction(name,params)ifname==nilthenreturn'Module:Location map/data/'..mapelseifmapData[name]==nilthenreturn''elseifparamsthenreturnmw.message.newRawMessage(tostring(mapData[name]),unpack(params)):plain()elsereturnmapData[name]endendelseifmw.title.new('Template:Location map '..map).existsthenlocalcache={}iftype(frame)~='table'ortype(frame.expandTemplate)~='function'thenerror('A frame must be provided when using a legacy location map')endreturnfunction(name,params)ifparamsthenreturnframe:expandTemplate{title='Location map '..map,args={name,unpack(params)}}elseifname==nilthenreturn'Template:Location map '..mapelseifcache[name]==nilthencache[name]=frame:expandTemplate{title='Location map '..map,args={name}}endreturncache[name]endendelseerror('Unable to find the specified location map definition. Neither "Module:Location map/data/'..map..'" nor "Template:Location map '..map..'" exists',2)endendfunctionp.data(frame,args,map)ifnotargsthenargs=getArgs(frame,{frameOnly=true})endifnotmapthenmap=p.getMapParams(args[1],frame)endlocalparams={}fork,vinipairs(args)doifk>2thenparams[k-2]=vendendreturnmap(args[2],#params~=0andparams)endlocalhemisphereMultipliers={longitude={W=-1,w=-1,E=1,e=1},latitude={S=-1,s=-1,N=1,n=1}}localfunctiondecdeg(degrees,minutes,seconds,hemisphere,decimal,direction)ifdecimalthenifdegreesthenerror('Decimal and DMS degrees cannot both be provided for '..direction,2)elseifminutesthenerror('Minutes can only be provided with DMS degrees for '..direction,2)elseifsecondsthenerror('Seconds can only be provided with DMS degrees for '..direction,2)elseifhemispherethenerror('A hemisphere can only be provided with DMS degrees for '..direction,2)endlocalretval=tonumber(decimal)ifretvalthenreturnretvalenderror('The value "'..decimal..'" provided for '..direction..' is not valid',2)elseifsecondsandnotminutesthenerror('Seconds were provided for '..direction..' without minutes also being provided',2)elseifnotdegreesthenifminutesthenerror('Minutes were provided for '..direction..' without degrees also being provided',2)elseifhemispherethenerror('A hemisphere was provided for '..direction..' without degrees also being provided',2)endreturnnilenddecimal=tonumber(degrees)ifnotdecimalthenerror('The degree value "'..degrees..'" provided for '..direction..' is not valid',2)elseifminutesandnottonumber(minutes)thenerror('The minute value "'..minutes..'" provided for '..direction..' is not valid',2)elseifsecondsandnottonumber(seconds)thenerror('The second value "'..seconds..'" provided for '..direction..' is not valid',2)enddecimal=decimal+(minutesor0)/60+(secondsor0)/3600ifhemispherethenlocalmultiplier=hemisphereMultipliers[direction][hemisphere]ifnotmultiplierthenerror('The hemisphere "'..hemisphere..'" provided for '..direction..' is not valid',2)enddecimal=decimal*multiplierendreturndecimalend-- Finds a parameter in a transclusion of {{Coord}}.localfunctioncoord2text(para,coord)-- this should be changed for languages which do not use Arabic numerals or the degree signlocalresult=mw.text.split(mw.ustring.match(coord,'%-?[%.%d]+°[NS] %-?[%.%d]+°[EW]')or'','[ °]')ifpara=='longitude'thenresult={result[3],result[4]}endifnottonumber(result[1])ornotresult[2]thenreturnerror('Malformed coordinates value',2)endreturntonumber(result[1])*hemisphereMultipliers[para][result[2]]end-- effectively make removeBlanks false for caption and maplink, and true for everything else-- if useWikidata is present but blank, convert it to false instead of nil-- p.top, p.bottom, and their callers need to use thisfunctionp.valueFunc(key,value)ifvaluethenvalue=mw.text.trim(value)endifvalue~=''orkey=='caption'orkey=='maplink'thenreturnvalueelseifkey=='useWikidata'thenreturnfalseendendlocalfunctiongetContainerImage(args,map)ifargs.AlternativeMapthenreturnargs.AlternativeMapelseifargs.reliefandmap('image1')~=''thenreturnmap('image1')elsereturnmap('image')endendfunctionp.top(frame,args,map)ifnotargsthenargs=getArgs(frame,{frameOnly=true,valueFunc=p.valueFunc})endifnotmapthenmap=p.getMapParams(args[1],frame)endlocalwidthlocaldefault_as_number=tonumber(mw.ustring.match(tostring(args.default_width),"%d*"))ifnotargs.widththenwidth=round((default_as_numberor240)*(tonumber(map('defaultscale'))or1))elseifmw.ustring.sub(args.width,-2)=='px'thenwidth=mw.ustring.sub(args.width,1,-3)elsewidth=args.widthendlocalwidth_as_number=tonumber(mw.ustring.match(tostring(width),"%d*"))or0;ifwidth_as_number==0then-- check to see if width is junk. If it is, then use default calculationwidth=round((default_as_numberor240)*(tonumber(map('defaultscale'))or1))width_as_number=tonumber(mw.ustring.match(tostring(width),"%d*"))or0;endifargs.max_width~=""andargs.max_width~=nilthen-- check to see if width bigger than max_widthlocalmax_as_number=tonumber(mw.ustring.match(args.max_width,"%d*"))or0;ifwidth_as_number>max_as_numberandmax_as_number>0thenwidth=args.max_width;endendlocalretval=args.float=='center'and'<div class="center">'or''ifargs.captionandargs.caption~=''andargs.border~='infobox'thenretval=retval..'<div class="noviewer thumb 'ifargs.float=='"left"'orargs.float=='left'thenretval=retval..'tleft'elseifargs.float=='"center"'orargs.float=='center'orargs.float=='"none"'orargs.float=='none'thenretval=retval..'tnone'elseretval=retval..'tright'endretval=retval..'"><div class="thumbinner" style="width:'..(width+2)..'px'ifargs.border=='none'thenretval=retval..';border:none'elseifargs.borderthenretval=retval..';border-color:'..args.borderendretval=retval..'"><div style="position:relative;width:'..width..'px'..(args.border~='none'and';border:1px solid lightgray">'or'">')elseretval=retval..'<div style="width:'..width..'px;'ifargs.float=='"left"'orargs.float=='left'thenretval=retval..'float:left;clear:left'elseifargs.float=='"center"'orargs.float=='center'thenretval=retval..'float:none;clear:both;margin-left:auto;margin-right:auto'elseifargs.float=='"none"'orargs.float=='none'thenretval=retval..'float:none;clear:none'elseretval=retval..'float:right;clear:right'endretval=retval..'"><div style="width:'..width..'px;padding:0"><div style="position:relative;width:'..width..'px">'endlocalimage=getContainerImage(args,map)retval=string.format('%s[[File:%s|%spx|%s%s]]',retval,image,width,args.altor((args.labelormw.title.getCurrentTitle().text)..' is located in '..map('name')),args.maplinkand('|link='..args.maplink)or'')ifargs.captionandargs.caption~=''thenifmw.ustring.find(args.caption,'##')thenretval=retval..'[[Category:Pages using location map with a double number sign in the caption]]'endendifargs.overlay_imagethenreturnretval..'<div style="position:absolute;top:0;left:0">[[File:'..args.overlay_image..'|'..width..'px]]</div>'elsereturnretvalendendfunctionp.bottom(frame,args,map)ifnotargsthenargs=getArgs(frame,{frameOnly=true,valueFunc=p.valueFunc})endifnotmapthenmap=p.getMapParams(args[1],frame)endlocalretval='</div>'ifnotargs.captionorargs.border=='infobox'thenifargs.borderthenretval=retval..'<div>'elseretval=retval..'<div style="font-size:90%;padding-top:3px">'endretval=retval..(args.captionor(args.labelormw.title.getCurrentTitle().text)..' ('..map('name')..')')..'</div>'elseifargs.caption~=''then-- This is not the pipe trick. We're creating a link with no text on purpose, so that CSS can give us a nice imageretval=retval..'<div class="thumbcaption"><div class="magnify">[[:File:'..getContainerImage(args,map)..'| ]]</div>'..args.caption..'</div>'endifargs.switcherLabelthenretval=retval..'<span class="switcher-label" style="display:none">'..args.switcherLabel..'</span>'elseifargs.autoSwitcherLabelthenretval=retval..'<span class="switcher-label" style="display:none">Show map of '..map('name')..'</span>'endretval=retval..'</div></div>'ifargs.caption_undefinedthenmw.log('Removed parameter caption_undefined used.')localparent=frame:getParent()ifparentthenmw.log('Parent is '..parent:getTitle())endmw.logObject(args,'args')retval=retval..'[[Category:Location maps with removed parameters|caption_undefined]]'endifmap('skew')~=''ormap('lat_skew')~=''ormap('crosses180')~=''ormap('type')~=''thenmw.log('Removed parameter used in map definition '..map())localkey=(map('skew')~=''and'skew'or'')..(map('lat_skew')~=''and'lat_skew'or'')..(map('crosses180')~=''and'crosses180'or'')..(map('type')~=''and'type'or'')retval=retval..'[[Category:Location maps with removed parameters|'..key..' ]]'endifstring.find(map('name'),'|',1,true)thenmw.log('Pipe used in name of map definition '..map())retval=retval..'[[Category:Location maps with a name containing a pipe]]'endifargs.float=='center'thenretval=retval..'</div>'endreturnretvalendlocalfunctionmarkOuterDiv(x,y,imageDiv,labelDiv)returnmw.html.create('div'):cssText('position:absolute;top:'..round(y,3)..'%;left:'..round(x,3)..'%'):node(imageDiv):node(labelDiv)endlocalfunctionmarkImageDiv(mark,marksize,label,link,alt,title)localbuilder=mw.html.create('div'):cssText('position:absolute;left:-'..round(marksize/2)..'px;top:-'..round(marksize/2)..'px;line-height:0'):attr('title',title)ifmarksize~=0thenbuilder:wikitext(string.format('[[File:%s|%dx%dpx|%s|link=%s%s]]',mark,marksize,marksize,label,link,altand('|alt='..alt)or''))endreturnbuilderendlocalfunctionmarkLabelDiv(label,label_size,label_width,position,background,x,marksize)iftonumber(label_size)==0thenreturnmw.html.create('div'):cssText('font-size:0%;position:absolute'):wikitext(label)endlocalbuilder=mw.html.create('div'):cssText('font-size:'..label_size..'%;line-height:110%;position:absolute;width:'..label_width..'em')localdistance=round(marksize/2+1)localspanCssifposition=='top'then-- specified topbuilder:cssText('bottom:'..distance..'px;left:'..(-label_width/2)..'em;text-align:center')elseifposition=='bottom'then-- specified bottombuilder:cssText('top:'..distance..'px;left:'..(-label_width/2)..'em;text-align:center')elseifposition=='left'or(tonumber(x)>70andposition~='right')then-- specified left or autodetected to leftbuilder:cssText('top:-0.75em;right:'..distance..'px;text-align:right')spanCss='float:right'else-- specified right or autodetected to rightbuilder:cssText('top:-0.75em;left:'..distance..'px;text-align:left')spanCss='float:left'endbuilder=builder:tag('div'):css('display','inline'):cssText('padding:1px'):cssText(spanCss):wikitext(label)ifbackgroundthenbuilder:cssText('background-color:'..background)builder:cssText('color:'..'inherit')endreturnbuilder:done()endlocalfunctiongetX(longitude,left,right)localwidth=(right-left)%360ifwidth==0thenwidth=360endlocaldistanceFromLeft=(longitude-left)%360-- the distance needed past the map to the right equals distanceFromLeft - width. the distance needed past the map to the left equals 360 - distanceFromLeft. to minimize page stretching, go whichever way is shorterifdistanceFromLeft-width/2>=180thendistanceFromLeft=distanceFromLeft-360endreturn100*distanceFromLeft/widthendlocalfunctiongetY(latitude,top,bottom)return100*(top-latitude)/(top-bottom)endfunctionp.mark(frame,args,map)ifnotargsthenargs=getArgs(frame,{wrappers='Template:Location map~'})endlocalmapnames={}ifnotmapthenifargs[1]thenmap={}formapnameinmw.text.gsplit(args[1],'#',true)domap[#map+1]=p.getMapParams(mw.ustring.gsub(mapname,'^%s*(.-)%s*$','%1'),frame)mapnames[#mapnames+1]=mapnameendif#map==1thenmap=map[1]endelsemap=p.getMapParams('World',frame)args[1]='World'endendiftype(map)=='table'thenlocaloutputs={}localoldargs=args[1]fork,vinipairs(map)doargs[1]=mapnames[k]outputs[k]=tostring(p.mark(frame,args,v))endargs[1]=oldargsreturntable.concat(outputs,'#PlaceList#')..'#PlaceList#'endlocalx,y,longitude,latitudelongitude=decdeg(args.lon_deg,args.lon_min,args.lon_sec,args.lon_dir,args.long,'longitude')latitude=decdeg(args.lat_deg,args.lat_min,args.lat_sec,args.lat_dir,args.lat,'latitude')ifargs.excludefromthen-- If this mark is to be excluded from certain maps entirely (useful in the context of multiple maps)forexclusionmapinmw.text.gsplit(args.excludefrom,'#',true)do-- Check if this map is excluded. If so, return an empty string.ifargs[1]==exclusionmapthenreturn''endendendifargs.coordinatesthen--Temporarily removed to facilitate infobox conversion. See [[Wikipedia:Coordinates in infoboxes]]--if longitude or latitude then--error('Coordinates from [[Module:Coordinates]] and individual coordinates cannot both be provided')--endlongitude=coord2text('longitude',args.coordinates)latitude=coord2text('latitude',args.coordinates)elseifnotlongitudeandnotlatitudeandargs.useWikidatathen-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't.localentity=mw.wikibase.getEntity()ifentityandentity.claimsandentity.claims.P625andentity.claims.P625[1].mainsnak.snaktype=='value'thenlocalvalue=entity.claims.P625[1].mainsnak.datavalue.valuelongitude,latitude=value.longitude,value.latitudeendendifnotlongitudethenerror('No value was provided for longitude')elseifnotlatitudethenerror('No value was provided for latitude')endlocalbuilder=mw.html.create()if(notargs.lon_deg)~=(notargs.lat_deg)thenbuilder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Degrees]]')elseif(notargs.lon_min)~=(notargs.lat_min)thenbuilder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Minutes]]')elseif(notargs.lon_sec)~=(notargs.lat_sec)thenbuilder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Seconds]]')elseif(notargs.lon_dir)~=(notargs.lat_dir)thenbuilder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Hemisphere]]')elseif(notargs.long)~=(notargs.lat)thenbuilder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Decimal]]')endifargs.skeworargs.lon_shiftorargs.markhighthenmw.log('Removed parameter used in invocation.')localparent=frame:getParent()ifparentthenmw.log('Parent is '..parent:getTitle())endmw.logObject(args,'args')localkey=(args.skewand'skew'or'')..(args.lon_shiftand'lon_shift'or'')..(args.markhighand'markhigh'or'')builder:wikitext('[[Category:Location maps with removed parameters|'..key..' ]]')endifmap('x')~=''thenx=tonumber(mw.ext.ParserFunctions.expr(map('x',{latitude,longitude})))elsex=tonumber(getX(longitude,map('left'),map('right')))endifmap('y')~=''theny=tonumber(mw.ext.ParserFunctions.expr(map('y',{latitude,longitude})))elsey=tonumber(getY(latitude,map('top'),map('bottom')))endif(x<0orx>100ory<0ory>100)andnotargs.outsidethenmw.log('Mark placed outside map boundaries without outside flag set. x = '..x..', y = '..y)localparent=frame:getParent()ifparentthenmw.log('Parent is '..parent:getTitle())endmw.logObject(args,'args')localkey=frame:preprocess('{{FULLPAGENAME}}')builder:wikitext('[[Category:Location maps with marks outside map and outside parameter not set|'..key..' ]]')endlocalmark=args.markormap('mark')ifmark==''thenmark='Red pog.svg'endlocalmarksize=tonumber(args.marksize)ortonumber(map('marksize'))or8localimageDiv=markImageDiv(mark,marksize,args.labelormw.title.getCurrentTitle().text,args.linkor'',args.alt,args[2])locallabelDivifargs.labelandargs.position~='none'thenlabelDiv=markLabelDiv(args.label,args.label_sizeor90,args.label_widthor6,args.position,args.background,x,marksize)endreturnbuilder:node(markOuterDiv(x,y,imageDiv,labelDiv))endlocalfunctionswitcherSeparate(s)ifs==nilthenreturn{}endlocalretval={}foriinstring.gmatch(s..'#','([^#]*)#')doi=mw.text.trim(i)retval[#retval+1]=(i~=''andi)endreturnretvalendfunctionp.main(frame,args,map)localcaption_list={}ifnotargsthenargs=getArgs(frame,{wrappers='Template:Location map',valueFunc=p.valueFunc})endifargs.useWikidata==nilthenargs.useWikidata=trueendifnotmapthenifargs[1]thenmap={}formapnameinstring.gmatch(args[1],'[^#]+')domap[#map+1]=p.getMapParams(mw.ustring.gsub(mapname,'^%s*(.-)%s*$','%1'),frame)endifargs['caption']thenifargs['caption']==""thenwhile#caption_list<#mapdocaption_list[#caption_list+1]=args['caption']endelseforcaptioninmw.text.gsplit(args['caption'],'##',true)docaption_list[#caption_list+1]=captionendendendif#map==1thenmap=map[1]endelsemap=p.getMapParams('World',frame)endendiftype(map)=='table'thenlocalaltmaps=switcherSeparate(args.AlternativeMap)if#altmaps>#mapthenerror(string.format('%d AlternativeMaps were provided, but only %d maps were provided',#altmaps,#map))endlocaloverlays=switcherSeparate(args.overlay_image)if#overlays>#mapthenerror(string.format('%d overlay_images were provided, but only %d maps were provided',#overlays,#map))endif#caption_list>#mapthenerror(string.format('%d captions were provided, but only %d maps were provided',#caption_list,#map))endlocaloutputs={}args.autoSwitcherLabel=truefork,vinipairs(map)doargs.AlternativeMap=altmaps[k]args.overlay_image=overlays[k]args.caption=caption_list[k]outputs[k]=p.main(frame,args,v)endreturn'<div class="switcher-container">'..table.concat(outputs)..'</div>'elsereturnp.top(frame,args,map)..tostring(p.mark(frame,args,map))..p.bottom(frame,args,map)endendreturnp