Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
Cultopedia
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Module:OSM Location map
Module
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
require('strict') local delink=require('Module:Delink').delink local getArgs = require('Module:Arguments').getArgs local p = {} local maplist={} local sgNames={} local highlightOption=false local highlightNum local visibleLinks local geoTotalPop = 0 local captionTable = '' local hasWikibase = mw.wikibase and type(mw.wikibase.isValidEntityId) == 'function' local captionTableText = {} -- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations, -- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link -- to an interactive fullscreen version, which has locator dots instead of annotations and shapes. -- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor -- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023. -- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before. -- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}} -- In general the css output from the two formats will be identical, but the the concise version will allow bits of -- greater control over some of the settings. -- see the documentation on the two template pages for details of how to use the mapping features. -- If language customisation is needed, there are text items below that can be translated. Also see the color table -- below with details of how to add additional color names to allow localised alternatives. -- (Translating other language shape-types could be possible, but has not currently been contemplated. -- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility -- with template calls already written using English). -- -------------- the following text variables are used within the template, and can be translated as required --------------- -- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase -------------- local negativeAnswer={no=1,'0'-1,off=1} local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles' local toggletext = 'List of numbered map items:' local termsOfUse = 'Maps: terms of use' local aboutOSM = 'About OpenStreetMaps' local yards = 'yds' local miles = 'miles' local metres = 'm' local km = 'km' local sqkm = 'sq.km' local year = 'year' local notFound = 'not found' local totalItems = 'Total' local sumOfDatapoints = 'sum of datapoints' local numberCol = 'No.' -- abbreviated for top of column of code numbers local wikidataLink = 'Wikidata page' local shapeList={} --This sets up the 'factoryDefault' shape group 0 shapeList["0"]={shapeType="0", Name="initialSettings", Parent="0", --sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg", --sgb items for border of the shape outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid", --sgc items text settings for labels textSZ="11px", textCL="darkgrey", textNG="0deg", --sgf further text settings textSP="0px", textLH="120%", textOL="0px", textBG="transparent", --sgd items for dotTag text settings tagSize="10px", tagColor="white", tagSpacer="0px", tagAngle="0deg", --sge items for extension line to connect label to dot textEW="0px", textEC="darkgrey", textES="solid" } local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution. colorList['red']='hardred' -- colorList ['source'] = target colorList['white']='white' -- converts any color that includes 'source' into its equivelent target colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it, colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones colorList['grey']='hardgrey' colorList['gray']='hardgrey' colorList['purple']='hardpurple' colorList['orange']='hardorange' colorList['leaf']='hardleaf' --for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values. CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e" CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205" CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527" CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21" CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433" CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"] CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910" CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8" CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05" CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039" CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC" CTB["beige"]=CTB["beigeground"] CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF" CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080" CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4" CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6" -- set up a table of predefined clip-paths local pathshape={} pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z" pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z" pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z" pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z" pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z" pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0" pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z" pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z" pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z" pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z" pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z" pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z" pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z" pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z" pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z" pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z" pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z" pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z" pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z" pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z" pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z" pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z" pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z" pathshape.boxd=pathshape.squared pathshape.boxdd=pathshape.squaredd pathshape.ellipsed=pathshape.circled pathshape.ellipsedd=pathshape.circledd local msg={} local function debugmsg(txt) table.insert(msg,txt) end local pmsg={} local function previewMsg(txt) table.insert(pmsg,txt) end local function colorLookup(color) for c,l in pairs(colorList) do if string.find(color,c) then return l end end return color end local function getColor (color,chk) local c local opacity="100" if not color or color=='' then color='hardgrey' end if color=="transparent" then return color end if color=="background1" then color='background' end if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then c=color elseif string.byte(color,1,1)==35 and #color == 4 then c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f' else local s=color..'1' s= s:sub(0,s:find("%d")-1) opacity=string.match(color,"%d+") if not CTB[s] then s= colorList[s] end -- check for synonyms and translations if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end c=CTB[s] or CTB.hardgrey end if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55))) c=c..hexval end return c end function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}} local c if not frame.args.color or frame.args.color=='' then c='grey' else c=string.lower(string.gsub(frame.args.color,'%s+','')) end return string.upper(string.sub(getColor(c),2)) end local function checkColors(color) local c=getColor(color,'check') local opacity =1 -- calculate colour brightness and return black or white for contrast if c=='transparent' then return c,'#000000',0 end if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end local r=tonumber('0x'..(string.sub(c,2,3)))/255 local g=tonumber('0x'..(string.sub(c,4,5)))/255 local b=tonumber('0x'..(string.sub(c,6,7)))/255 if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity end end local function morethan(a,b) if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') ) b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0') end return a>b end local function lessthan(a,b) if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]')) b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0') end return a<b end local function getsize(size) --size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding --eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers local sizeval = {} for v in string.gmatch(size, "[^px]+") do table.insert(sizeval,v) end sizeval[1] = tonumber(sizeval[1]) or 13 sizeval[2] = tonumber(sizeval[2]) or sizeval[1] sizeval[3] = tonumber(sizeval[3]) or 0 return sizeval[1],sizeval[2],sizeval[3] end local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values -- and converts compass points to minus or not-minus, return with separating comma. local lat = string.match(coord,'[%.%d]+°[NS]') local lon = string.match(coord,'[%.%d]+°[EW]') local neg={N="",S="-",W="-",E=""} return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+') end local function convertCoordsTrad (row) local coords='' if row and string.find (row,'<span class="geo">') then local a,b=string.find(row,'<span class="geo">') local start=b+1 a,b=string.find(row,"</span>",b) local finish=a-1 coords=string.sub(row,start,finish) coords=string.gsub(coords,'; ',',') end return coords end local function convertCoords (row) local start,finish,lat,lon,coords,says if row then local a,b=string.find(row,"<span class=") start=a while a do -- find the final span> finish=b a,b=string.find(row,"span>",b) end if start then coords= string.sub(row,start,finish) says="" if string.find(coords,'<span class="error">') then error("coord error: badly formed coordinates",0) end coords=coord2text(coords) coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1) else coords=row end return coords else return "Nothing to see here" end end local function fillCommas(val,max) local line='' if not val then line=',' -- ensure there is some content else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces end if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries line=line..string.rep(',',max-count) while(string.find(line,",,") ) do line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces end return line end local function makeLinkBox(left,top,wid,label, link) local linkBoxName='Transparent square.svg' if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end local builder = mw.html.create('div') --display:inline-block; local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line if t then label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link) end builder :cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px') :wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label )) return tostring(builder) end local function extractItem(row,searchItem) -- remove text following a searchItem or start of line, which might be in quote-marks to allow commas local xend,xstart=1,0 if not row then return '','' end if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces xstart=xstart+1 end local xbyte=row:byte(xstart+1) if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes xstart=xstart+1 xend=row:find(string.char(xbyte),xstart+1) else xend = row:find(',',xstart+1) -- if no quotes, we assume no commas if not xend then xend=#row+1 end end -- return residual row and extracted text return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1) end local function itemCheck(item,ext) if not item then return nil end if not ext then ext='' end return (string.match(item,"[%.%-?%d]+") or '0')..ext end local function stripdivs(line) return string.gsub(line or '',"%b<>", ' ') end local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil local r={} local x=1 item=string.lower(fillCommas(item,max)) for t in string.gmatch(item,"[^,]+") do r[x]=string.gsub(t,"%s+","") if r[x]=='' then r[x]= nil else -- residual items might have commas if x>max then r[max]=(r[max] or '')..', '..r[x] end end x=x+1 end return r end local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters --[[ shape table items and default values as set at top of page shapeType="0", Name="initialSettings", Parent="0", --sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg", --sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid", --sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic") --sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg", --sge extension line textEW="1px", textEC="darkblue", textES="solid" --sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground", <!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid | sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg | sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background | dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename--> --]] if args["sgn"..sgval] then local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)") sgNames[sgname]=sgval end local parent= args["sgp"..sgval] if parent then parent=string.match(parent,"(%w+)(.*)") local pos= string.find(parent,"%d+") if pos == 1 then parent=string.match(parent,"%d+") else parent=sgNames[parent] or '1' end end if sgval~='H' then if not parent or tonumber(parent) > tonumber(sgval) then if sgval=="1" then parent="0" else parent="1" end end end local itemTab, line, filename result[sgval]={} result[sgval].shapeType=sgval line,filename=extractItem(args['sga'..sgval] or '','image:') if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga result[sgval].shapeFile=filename or '' -- sga= Attributes for shape itemTab=splitItem(line,4) result[sgval].shape = itemTab[1] or result[parent].shape result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle -- sgb= Border outline attributes for shape itemTab=splitItem(args['sgb'..sgval],3) result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle --sgc=character attributes for label itemTab=splitItem(args['sgc'..sgval],4) result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic --sgd=dotTag attributes itemTab=splitItem(args['sgd'..sgval],4) result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize result[sgval].tagColor=itemTab[2] or result[parent].tagColor result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle --sge= extension line attributes itemTab=splitItem(args['sge'..sgval],4) result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour result[sgval].textES=itemTab[3] or result[parent].textES -- style --sgf= fx for label text itemTab=splitItem(args['sgf'..sgval],4) result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background return result end local function round(x,dec) -- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated) if (dec or 0)==0 then return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0 end dec =10^(dec) return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec end local function maptogrid(t,r,test) --[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level. t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom} output is two values, x and y, rounded to r decimal places--]] local x,y x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90))) y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90))) return round(x,r),round(y,r) --source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator --[[ width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase. Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon, zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2. Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase, scale the resulting meter-offset to pixels, add it to height/2. A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is in the same place as if that location is positioned at the centre. Original Python code for lat,lon to x,y where 0,0 is the centre of the map print('x=',width+(((math.radians(lon1) * 6378137)- (math.radians(lonbase) * 6378137))/ (156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+(( (math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)- (math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/ (156543.03*math.cos(latbase/180)/(2**zoom))) ) --]] end local function getScale(zoom, lat,magVal) zoom = tonumber(zoom) or 1 lat = tonumber(lat) or 0 if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17 local y if dist < 1 then y=(round(dist*10,1)) return tostring(y*100)..metres, tostring(round(y*109,0))..yards elseif dist <18 then y=(round(dist,0)) return tostring(y)..km, tostring(round(y*0.621371,1))..miles elseif dist <500 then y=round(dist/10) return tostring(y*10)..km, tostring(round(y*6.21371,0))..miles else y=round(dist/100) return tostring(y*100)..km, tostring(round(y*62.1371,0))..miles end end local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters -- takes a structured series of comma-separated items which get parsed as the following: -- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag) -- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list -- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info -- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots -- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1 -- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot -- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below) -- quote marks are not needed unless including commas within the label text -- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1] -- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture) --<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename--> local result={} local count=1 local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon row=fillCommas(row,4) result.code=dotval -- store the parameter name as the id code for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present if count==1 then --see if it is a number or a name local pos= string.find(item,"%d+") if pos == 1 then result.group=string.lower(string.gsub(item,"%s+","")) else item=string.match(item,"(%w+)(.*)") -- ensure just a single word result.group=sgNames[item] end elseif count==2 then result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff elseif count==3 then result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0 elseif count==4 then result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas end count=count+1 end row, result.labelText= extractItem(args["dotlabel"..dotval]) result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks local item=splitItem(row,6) result.labelPos=item[2] or 'center' result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0 result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0 result.param1=string.lower(item[5] or '') if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360 elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360 end local txt = '' if item[6] then -- ensure all info elements are included, including commas count=1 local max=6 for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do if count>max then txt=txt..',' end if count>=max then txt=txt..t1 end count=count+1 end end result.info=txt result.imageName = args['dotpic'..dotval] -- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text local testx=args["dotlink"..dotval] or '' result.dotLink=testx if testx ~= '' then testx=stripdivs(testx) result.title=delink({ testx }) local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern) if linkstart then result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } ) else result.dlink='' end else result.dlink='' result.title='' end if args['dotfeature'..dotval] then local item=splitItem(args['dotfeature'..dotval],6) if (item[1] or '') =='photo-panel' then result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size result.photoImage=result.imageName result.posType='photo-panel' elseif (item[1] or '') == 'mark-line' then local x=tonumber(dotval or '0') result.markDest=item[5] or tostring(x-1) result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1') result.mlStyle= item[3] or 'solid' result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0') result.posType='mark-line' end --debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth)) end maplist.lon=result.lon maplist.lat=result.lat result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table return result end local function multiCheck (args, argName, argVal, defVal, alt) if not alt then alt='nonexistant' end if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end if argVal=='' then return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1 else return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal end end local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type) if type == 'header' then if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>' else if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>' end end local function formatInt(number) if number == 0 then return notFound end local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)') int = int:reverse():gsub("(%d%d%d)", "%1,") return minus .. int:reverse():gsub("^,", "") .. fraction end local function convertToSqKm (value,punit) if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres end return value end local function assignTradstyleShape(shapeResult,default,dotResult,args,nval) local item,itemTab local autoDotTag='' local shapeWidth,shapeHeight=0,0 local argval=nval -- to catch the unnumbered shape series local qvalue='' if argval=='0' then argval='' end if nval=='H' then shapeResult.H={} end -- Assign autovalues if a wikidata Qvalue has been supplied -- some preliminary documentation -- geo-data-type= default is P1082 (population) -- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046) -- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters -- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs -- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in -- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527) -- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters -- shapeD= sets default shape, which migh be n-circle to include numbers. -- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots) -- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'. -- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries) -- find-geo-dots=1 or 0 (needs to also include geo-region) -- label-posD = gets additional options: [off] gives no label, [on] will position lower centre, -- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then local pvalue = args['geo-data-type'] or 'P1082' local plabel = mw.wikibase.getLabel(pvalue) if not args['geo-number-size'] then args['geo-number-size']=12 end if not args['label-posD'] then args['label-posD'] = 'on' end qvalue=args['geo-data'..argval] -- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000 local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage() local thisPop=0 local areatxt='' local yearVal='' if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0 if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100) thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit) end if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year end else debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel)) end local scalefactor=args['geo-scalefactor'] or 1 if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end if not args['mark-size'..argval] then if args['show-geo-dots'] == '0' then args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to else if thisPop>0 then args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0) else args['mark-size'..argval] = args['mark-sizeD'] or '1' args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH'] end end end local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on')) args['label-pos'..argval] = posTemp posTemp=splitItem(posTemp,2)[1] if pvalue == 'P2046' then thisPop = round(thisPop,2) areatxt=sqkm..areatxt -- round decimals if sq km area elseif pvalue == 'P10622' then if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value if punit == 'Q4917' then areatxt=' US$' elseif punit == 'Q4916' then areatxt=' euro' elseif punit == 'Q25224' then areatxt=' £GBP' else areatxt=mw.wikibase.getLabel(punit)..areatxt end end end if posTemp=='off' then else if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end local tline=posTemp:find("[%+%^%-]") if tline then local tval=posTemp:sub(tline,tline) local eline posTemp=posTemp:sub(1,tline-1) if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to else eline = ' ' -- must be a dash, so no line break end args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')' args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval]) end end if posTemp=='on' then if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position args['label-pos'..argval]='center' local _,br = string.gsub(args['label'..argval],'%^','') local item=splitItem(args['label-size'..argval],2) args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2) end local nt='' if args['mark-title'..argval] == 'nolist' then nt='nolist,' end if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then if mw.wikibase.getSitelink(qvalue) then args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]' else args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue) end end -- If mark-title is not set locally then attempt Sitelink value, or use label if thisPop == 0 then areatxt='' end captionTableText[argval]={} captionTableText[argval][1]= args['mark-title'..argval] captionTableText[argval][2]= formatInt(thisPop) if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end if args['show-q-values'] == '1' then captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>' else local nbsp = ' ' if maplist.width < 230 then nbsp = ' ' end captionTableText[argval][4]='' if captionTableText[argval][3] == 'no-year' then captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>' else captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>' end end if args['mark-title'..argval] == 'none' then -- no title else args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')' end if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value end item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values else item = multiCheck(args,'shape',argval,'image') if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values end if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then autoDotTag=string.sub(item,0,1) item=string.sub(item,3) end if item == 'image' then shapeResult[nval].shape = 'image:' shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg') shapeWidth=-1 else shapeResult[nval].shape = item end item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px')) if b==a and args['mark-dim'..argval] then b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+")) end shapeHeight=b/2 item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px' shapeResult[nval].shapeSize= item itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2) shapeResult[nval].shapeColor=itemTab[1] or 'hardred' item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed if not item then item=itemCheck(args['shape-opacity'..argval],'%') end if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg' if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end --sort out the outline entry itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4) shapeResult[nval].outlineColor=itemTab[1] or 'dark grey' shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px' if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%') end shapeResult[nval].outlineStyle=itemTab[4] or 'solid' -- label size, background, outline shapeResult[nval].textAT=args['label-attribute'..argval] or '' itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3) shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px' if itemTab[2]=='outline' then shapeResult[nval].textBG=itemTab[3] or 'transparent' shapeResult[nval].textOL='1px' elseif itemTab[3]=='outline' then shapeResult[nval].textBG=itemTab[2] or 'transparent' shapeResult[nval].textOL='1px' else shapeResult[nval].textOL='0px' shapeResult[nval].textBG=itemTab[2] or 'transparent' end if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end --label color etc itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2) shapeResult[nval].textCL=itemTab[1] or 'darkgrey' if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg') if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end --sgd=dotTag attributes shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px' local c1,c2=checkColors(shapeResult[nval].shapeColor) shapeResult[nval].tagColor=c2 shapeResult[nval].tagSpacer='0px' shapeResult[nval].tagAngle='0deg' -- sge extension line attributes local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6) if shapePos[2]=='with-line' or shapePos[2]=='n-line' then shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey') elseif shapePos[2]=='photo-panel' then shapeResult[nval].textEW='2px' -- width shapeResult[nval].textEC=shapeResult[nval].textCL else shapeResult[nval].textEW='0px' -- width shapeResult[nval].textEC='grey'-- colour end shapeResult[nval].textES='solid' if argval=='H' then return dotResult end --Assign dot values local dotItem={} dotItem.group=nval dotItem.code=nval dotItem.posType=shapePos[2] or 'nil' if (shapePos[2] or '') =='photo-panel' then dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+")) dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") ) dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0) dotItem.photoImage=args['mark-image'..argval] --debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth)) end if (shapePos[2] or '') =='mark-line' then local x=tonumber(argval or '1') dotItem.markDest=shapePos[6] or tostring(x-1) dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+")) dotItem.mlStyle= shapePos[4] or 'solid' dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+")) shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey' end if args['mark-coord'..argval] then itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2) if itemTab[1] == nil or itemTab[2] == nil then debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval)) else dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0 dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0 end else dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0 dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0 end if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360) end maplist.lon=dotItem.lon maplist.lat=dotItem.lat dotItem.gridx, dotItem.gridy = maptogrid(maplist,1) local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink if item=='none' then dotItem.param1='nomap nolist' item='' end if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end dotItem.dotLink=item if item ~= '' then item=stripdivs(item) dotItem.title=delink({item}) local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern) if linkstart then dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'}) -- debugmsg('dlink for '..item..' is '..dotItem.dlink) else dotItem.dlink='' end else dotItem.dlink='' dotItem.title='' end if autoDotTag=='n' then item=nval elseif autoDotTag=='l' then item=string.char(64+tonumber(nval)) else item='' end dotItem.dotTag = args['numbered'..argval] or item if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then if dotItem.dlink =='' then item=dotItem.dotTag..' '..args['label'..argval] else item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval] end else item=(args['label'..argval] or '') end if args['labela'..argval] then item = item..'^'..args['labela'..argval] end if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end dotItem.labelText = a -- convert hats to line breaks if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0' else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0' end dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+")) if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0' else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0' end dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+")) dotItem.labelPos=shapePos[1] if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end dotItem.info=args['mark-description'..argval] or '' table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying -- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize) return dotResult end local function tradstyleParseShapes(args,dotTable,dotmax) local sgNumbers,sgSortable={},{} for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used local x=tostring(argindex) if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then -- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0' args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0' local p=args['geo-data-type'] or 'P1082' if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0 if p == 'P2046' then -- area, so convert to sq km if needed (/100) n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit) end geoTotalPop = geoTotalPop + n end else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x])) end end if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then sgNumbers[x]=x -- add it to the list end end for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly) if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list local default={} default.D={} local dotResult={} for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards shapeList[sgnum]={} dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum) end dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem local unitConvert=1 local areaUnits='' if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage() local pvalue = args['geo-data-type'] or 'P1082' local plabel = mw.wikibase.getLabel(pvalue) local subhead='' -- Assemble the subhead content, a line of text about the region as a whole if region and mw.wikibase.getBestStatements(region,pvalue)[1] then local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value if pvalue == 'P10622' then -- add other currency-based pvalues here areaUnits=mw.wikibase.getLabel(punit) end if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies () elseif punit == 'Q4916' then areaUnits=' euro' elseif punit == 'Q25224' then areaUnits=' £GBP' end -- if pvalue == 'P2046' then -- may need to add other instances of sq km so add in other P-items here if they need units showing -- end geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert local yearVal if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5) if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end end yearVal = ' ('..yearVal..')' else yearVal ='' end end subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal else if pvalue == 'P2046' then areaUnits = sqkm end subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits end if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end if maplist.width < 330 then numberCol = ' ' end if maplist.width < 280 then year = 'no-year' end captionTable = subhead if (args['show-q-values'] or '') ~= '1' then captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>' wikidataLink='' end captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>' captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header') end return dotTable end local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed local tshape,tlink = false,"" if dlink~='' and not nolabel then tlink=dlink end if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved return tshape,tlink end local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape local x=tonumber(string.match(angle,"%-?%d+")) if x<0 then x=360+x end -- set to a single degree direction, 0 to 360 if x>359 then return 0,0 end -- shift the centre of the triangle based on rotation value if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv elseif x >220 then return 0,-0.17 --triangle left= -shifth elseif x >40 then return 0,0.22 --triangle right= +shifth end return 0,0 end local function makeTriangle(result,row,shape,outline,tlink) local w,h,r=getsize(shape.shapeSize) if outline then local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) w=w+p*2 h=h+p*2 end table.insert(result,'<div ') if tlink then table.insert(result,' title="'..row.title..'" ') end table.insert(result,'style="display:inline-block; position: absolute') if shape.shapeAngle ~= '0deg' then table.insert(result,'; transform: rotate('..shape.shapeAngle..')') end local shiftv,shifth=0,0 shiftv,shifth=tshift(shape.shapeAngle) table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px') table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px') table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent') table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent') if outline then -- fill with outline colour, to make a 'base layer' or shape colour table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">') else table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">') end table.insert(result,'</div>') end local function makeSquare(result,row,shape,tshape) local w,h,r=getsize(shape.shapeSize) if shape.shape=='square' then h=w end -- squares are always square!. box can stretch local div=mw.html.create ('div') if tshape then -- Add tooltip if needed div:attr('title',row.title) end div:css('position', "absolute") if shape.outlineWidth ~= "0px" then div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor)) end if shape.shapeAngle ~= "0deg" then div:css('transform',"rotate("..shape.shapeAngle..")") end if r~=0 then div:css('border-radius',tostring(r).."px") end if shape.shape=='panel' then div:css('top', tostring(row.gridy).."px") div:css('left', tostring(row.gridx).."px") else div:css('top', tostring(row.gridy-h/2).."px") div:css('left', tostring(row.gridx-w/2).."px") end div:css('width', tostring(w).."px") div:css('height', tostring(h).."px") div:css('background-color', getColor(shape.shapeColor) ) div:css('color', 'inherit') table.insert(result,tostring(div)) end local function makeCircle(result,row,shape,tshape) local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch local div=mw.html.create ('div') if tshape then -- Add tooltip if needed div:attr('title',row.title) end div:css('position', "absolute") if shape.outlineWidth ~= "0px" then div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor)) end if shape.shapeAngle ~= "0deg" then div:css('transform',"rotate("..shape.shapeAngle..")") end div :css('top', tostring(row.gridy-h/2).."px") :css('left', tostring(row.gridx-w/2).."px") :css('width', tostring(w).."px") :css('height', tostring(h).."px") :css('border-radius', "50%") :css('background-color', getColor(shape.shapeColor) ) :css('color', 'inherit') table.insert(result,tostring(div)) end local function makeRuleA(result,row,shape) local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) local lineV=0 if shape.shape=='rulea' then lineV=oWid*3+16 end table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together table.insert(result,"; top:"..tostring(row.gridy - w/2).."px") table.insert(result,"; left:"..tostring(row.gridx - w/2).."px") table.insert(result,"; width:"..tostring(w).."px") table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;") table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" ) table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container table.insert(result,"; top:"..tostring(lineV).."px") table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px") table.insert(result,"; height: "..tostring(w -lineV).."px") table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor)) table.insert(result,"; background:transparent; color:inherit;\"></div>") if shape.shape=='rulea' then table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px") table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent") table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent") table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>") end table.insert(result,"</div>") end local function makeCurveA(result,row,shape) -- draw a curve with Arrow ----- local w,h=getsize(shape.shapeSize) -- = width,height local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+")) table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;') table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ') table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ') table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ') if shape.shape=='curvea' then table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">') else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">') end table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';') if shape.shape=='curvea' then table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;') else table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;') table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;') end -- and add a triangular arrow head table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ') if shape.shape=='curvea' then table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ') else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;') end -- reverse table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;') table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;') table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;') table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>') end local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape) table.insert(result,"<div style=\"display:inline-block; position: absolute;") -- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame -- Maths calculations thanks to ES local tw if double <= 1 then tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width else tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap end -- debugmsg(oWid..'+'..double..'/2='..tw) table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;") table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;") table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;") table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ") table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi -- if x1 == x2 then lineAngle=90 end table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>") -- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle) if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well -- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end if x2 < x1 then lineAngle=lineAngle-180 end if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead -- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle) end local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted -- return the text css div code to position and draw a shape occupying a specified clippath if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then pathshape.svgpath=string.sub(shape.shape,9) shape.shape='svgpath' end if not pathshape[shape.shape] then debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined')) return end local w,h,r=getsize(shape.shapeSize) if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size w=w*2.5 h=h*2.5 end if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes local shifth,shiftv = 0,0 if string.match(shape.shape,"triangle") then shiftv,shifth =tshift(shape.shapeAngle) end if outline then local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0 w=w+p*2 h=h+p*2 end table.insert(result,"<div ") if tshape then -- Add tooltip if needed table.insert(result," title=\""..row.title.."\" ") end table.insert(result,"style=\"display:inline-block; position: absolute; background-color:") if outline then table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer' else table.insert(result,getColor(shape.shapeColor)) end table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ") -- adds the required clippath data from the table of pathshape string literals table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px") table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px") table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes table.insert(result,"; height:20px; transform:") if shape.shapeAngle ~= "0deg" then table.insert(result," rotate("..shape.shapeAngle..")") end table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")") table.insert(result,"\"></div>") end local function makeImage(result,row,shape) local w,h,r=getsize(shape.shapeSize) local image=shape.shapeFile if not image or image=='' then image='Red pog.svg' end local imagediv=mw.html.create ('div') imagediv:css('position', "absolute") if shape.shapeAngle ~= "0deg" then imagediv:css('transform',"rotate("..shape.shapeAngle..")") end imagediv :css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images :css('left', (row.gridx-1-w/2).."px") :css('background-color', "transparent" ) :css('color','inherit') :wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]') table.insert(result,tostring(imagediv)) end local function makePhotoPanel(result,row,shape) local h=row.ppheight table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' ) table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;') table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;') table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>') if row.photoImage and row.photowidth >0 then table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;') if row.labelPos=='left' or string.find(row.labelPos,'west') then table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;') row.dx=row.dx+row.photowidth/2 else table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;') row.dx=row.dx-row.photowidth/2 end table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">') table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>') row.labelPos='center' end end local function makePanelText(result, row, shape) local w,h,r=getsize(shape.shapeSize) local ty=tonumber(string.match(shape.textSZ or '11',"%d+") ) table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';') if row.labelPos == 'left' or string.find(row.labelPos,'west') then table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;') table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;') elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;') table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);') elseif row.labelPos == 'top' or row.labelPos== 'north' then table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;') table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);') elseif row.labelPos == 'bottom' or row.labelPos=='south' then local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1 table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;') table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);') else -- center or centre local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6 table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;') table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);') end if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>") end local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem) local w,h,r=getsize(shape.shapeSize) table.insert(result,"<div ") if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end local ty,bry,linkoffset = 0,0,0 local compy=0 local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100 if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings ty=tonumber(string.match(shape.tagSize,"%d+")) or 0 table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62)) table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ") table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;") local trf="" if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";") if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline table.insert(result,'style="position:absolute; ') if dotItem==2 then -- dotTag is out at x,y so 85% ty=tonumber(string.match(shape.tagSize or '0',"%d+")) table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52)) else -- it is labelText, so use textLH or 120% ty=tonumber(string.match(shape.textSZ or '11',"%d+")) if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%? if row.posType == 'with-line' then bry=0 end if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half elseif row.labelPos=='top' or row.labelPos == 'north' then if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty else bry= -bry*ty+2 end else bry=-bry*(ty/2 * lh) end end if row.posType == 'photo-panel' then bry=bry+3 end table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ') table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2)) end table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ") table.insert(result,"width: max-content; ") local trf="" if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end if shape.textOL~="0px" then table.insert(result,"background-color: "..getColor(shape.textBG).."; ") table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;") else table.insert(result,"background-color: transparent;") end if row.labelPos=="right" or string.find(row.labelPos,'east') then table.insert(result,"text-align: left; ") linkoffset=w if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end elseif row.labelPos=="left" or string.find(row.labelPos,'west') then if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end linkoffset=-w table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ") else table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ") end table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;') end if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>') else table.insert(result,"\">"..textItem.."</div>") end if tlink~='' and not string.match(row.param1 or "","nolink") then table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink)) end end local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data local result={} local w,h,rndg=getsize(shape.shapeSize) local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") ) local align=row.labelPos or '' local offsetx,offsety=0,0 local ty=tonumber(string.match((shape.tagSize or 9),"%d+" )) if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" )) else align='center' end -- it is just for dotTag, so justify center -- identify align value and extend offsets local widthzone,heightzone = (w/2)+1,(h/2)+1 local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2) if align=='auto' or align=='' then if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1 elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1 elseif theta <0 then offsety=ty/2-1 -- bottom else offsety=0-ty/2+1 -- top end elseif align=='left' or string.find(align,'west') then row.dx=row.dx - w/2 widthzone=4 offsetx=-1 elseif align=='right' or string.find(align,'east') then row.dx=row.dx + w/2 widthzone=4 offsetx=1 elseif align=='top' or align=='north' then if string.find(shape.shape,'curve') then row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1 else row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1 end elseif align=='bottom' or align=='south' then if string.find(shape.shape,'curve') then row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1 else row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1 end end if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then -- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or '')) makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1) -- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC) end if row.posType and row.posType=='mark-line' then if row.gridx2 and row.gridy2 then makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape) -- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC)) end end if w ~= 0 then if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end if shape.shape=="triangle" then if shape.outlineWidth ~= "0px" then makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required end makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then makeSquare(result,row,shape,tshape) elseif shape.shape=="circle" or shape.shape=="ellipse" then makeCircle(result,row,shape,tshape) elseif string.find(shape.shape,'image:')==1 then makeImage(result,row,shape) elseif shape.shape=="rulea" or shape.shape=='rule' then makeRuleA(result,row,shape) elseif shape.shape=="curvea" or shape.shape=="curvec" then makeCurveA(result,row,shape) else -- use a pathshape clipPath if shape.outlineWidth ~= "0px" then makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required end makeClipPath(result,row,shape,false,tshape) end end if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape) else if row.dotTag and row.dotTag ~= "" then -- there is a dotTag if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1) else if row.labelText and row.labelText~='' then -- tag and label both used makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1) makeTextItem(result, row, shape, align, tlink, row.labelText, 0) else makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1) makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot end end else if (row.labelText and row.labelText~='') then -- just the label. No tag makeTextItem(result, row, shape, align, tlink, row.labelText, 0) end end end if tlink and tlink~='' then table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink)) end return table.concat(result) end local function getmapframecontent(args,use) local result, comma= {}, '' local propertyTable= {} local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2) if use == 'basemap' then table.insert(result, '[') propertyTable.stroke = "#000000" propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6') if color[2] then propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100 end else propertyTable.title = args['map-data-text'] or args['map-data'] or '' propertyTable.stroke = getColor(color[1]) propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6') propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100 end if args['map-data-inverse'] then local mapJson = mw.text.jsonEncode{ type = 'ExternalData', service = 'geomask', ids = args['map-data-inverse'], properties = {title = (args['map-data-text'] or ''), stroke = '#555555', fill = '#555555', ['fill-opacity'] = 0.1, ['stroke-width'] = 1, ['stroke-opacity'] = 0.5 } } table.insert(result, mapJson) comma=', ' end if args['map-data-heavy'] then local mapJson = mw.text.jsonEncode{ type = 'ExternalData', service = 'geoline', ids = args['map-data-heavy'], properties = { title = args['map-data-heavy'], stroke = propertyTable.stroke, ['stroke-width'] = 9, ['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 } } table.insert(result, comma .. mapJson) comma=', ' end if args['map-data-light'] and args['map-data-light'] ~= '' then local mapJson = mw.text.jsonEncode{ type = 'ExternalData', service = 'geoline', ids = args['map-data-light'], properties = { title = args['map-data-light'], stroke = propertyTable.stroke, ['stroke-width'] = 3, ['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 } } table.insert(result, comma .. mapJson) comma=', ' end if args['map-data'] then propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 local mapJson = mw.text.jsonEncode{ type = 'ExternalData', service = 'geoline', ids = args['map-data'], properties = propertyTable } table.insert(result, comma .. mapJson) comma=', ' end if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata local mapSparql = mw.text.jsonEncode{ type = 'ExternalData', service = args['map-wdqs-type'] or 'geopoint', query = args['map-wdqs'], } table.insert(result, comma .. mapSparql) -- debugmsg('mapSparql = ' .. mapSparql) comma=', ' end if args['map-raw'] then table.insert(result, comma .. args['map-raw']) end if use=='basemap' then table.insert(result,']') end return table.concat(result) end --eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px local function makeLocatorMap (args, result) local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH if args['mini-locator'] then pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box pos=itemlist[2] or 'right' miniW,miniH=getsize(itemlist[3]) miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100 miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100 miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+')) miniBox=miniBox*miniW/100 miniBH=miniBox * maplist.height/maplist.width elseif args['mini-file'] then miniFile = args['mini-file'] pos=string.lower(args.minimap or 'right') if pos=='file' then pos='right' end miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any, miniX, miniY=args['minipog-gx'],args['minipog-gy'] if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end if args['minipog-gx'] then miniBox=miniBox*miniW/100 end miniBH=miniBox * maplist.height/maplist.width else return end local miniTop,miniLeft=0,1 if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right miniTop=maplist.height+2-miniH if string.find(pos,'right') then miniTop=miniTop-15 -- to avoid (c) line miniLeft=maplist.width-1-miniW end end table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;') table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' ) table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>') if miniX and miniX>0 then if args['minipog-y'] then miniY=miniY/1.04 end if args['minipog-x'] then miniX=miniX/1.04 end if miniBox<1 then table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;') table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>') else table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;') table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>') end end end local function makeArcText(args,result,nval) local items, itemlist='',{} local arcText='' if args['arc'..nval] then items=convertCoords (args['arc'..nval]) items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse if itemlist[4]=='' then itemlist[4]='12' end itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+") if itemlist[5]=='' then itemlist[5]='grey' end itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+") itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+") itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+") end if args['arc-coord'..nval] then local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2) maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) else maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+")) maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+")) end local arcX,arcY=maptogrid(maplist,6) arcText = args['arc-text'..nval] or arcText local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12') local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') ) local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90 local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05') local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 ) arcRadius=450*arcRadius*0.75 local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1') local arcRotate =arcAngle+90 if arcGap<0 then arcRotate=arcAngle-90 end local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius) local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius) local step=1 for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do table.insert(result,'<div style="position: absolute;') local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;' local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;' table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);') table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';') table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>') step=step+1 end end local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor) local item={} itemdescription=stripdivs(itemdescription or '') local templon=lon if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot if itemcolor=='transparent' then itemcolor='white' end itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end table.insert(item, '{ "type": "Feature", "properties": {') table.insert(item, ' "title": "'..itemtitle..'",') table.insert(item, ' "description": "'..itemdescription) table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon)) table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",') table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },') table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }') return table.concat(item) end local function makeLegendBox(result,args) local legend ={} local line,count, maxWidth='',1,8 local item line,item=extractItem(args.legendBox or '') local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end legend.Text = a -- convert hats to line breaks line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options) legend.Size=line[2] or '150px80px1px' legend.Pos=line[3] or '10px10px' legend.Background=line[4] or 'beigeground' legend.Color=line[5] or 'darkbrown' legend.Param= line[6] or '' local argnum,legendCount,titleHeight='1',1,0 if (legend.Text and legend.Text~='') then titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", "")))) end local legendLine,legendGroup,legendY={},{},{} while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '') line=splitItem(line,3) legendGroup[legendCount]=line[2] or argnum if shapeList[legendGroup[legendCount]] then maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth) else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth) end maxWidth=maxWidth+1 if line[3] then legendY[legendCount]=tonumber(string.match(line[3],'[%d]+')) else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight -- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end end legendCount=legendCount+1 argnum=tostring(legendCount) end local w,h,r=getsize(legend.Size or '') local x,y=getsize(legend.Pos or '') local div=mw.html.create ('div') div:css('position', 'absolute') div:css('outline', '1px solid'..getColor(legend.Color)) if r~=0 then div:css('border-radius',tostring(r).."px") end div :css('top', y.."px") :css('left', x.."px") :css('width', w.."px") :css('height', h.."px") :css('line-height','105%') :css('background-color', getColor(legend.Background) ) :css('color','inherit') if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end div:tag( 'div' ) :css('position', 'absolute') :css('top','1px') :css('left', (w/2).."px") :css('width',(w-8)..'px') :css('text-align', 'center') :css('color', getColor(legend.Color)) :css('transform', 'translateX(-50%)') :css('font-size','11px') :wikitext(legend.Text) :done() for lct=1,legendCount-1 do --local t=legendGroup[lct] local shape=shapeList[legendGroup[lct]] or shapeList['1'] local row={} row.gridx=3+maxWidth/2 row.gridy=(legendY[lct] or 0) + 5 if shape.shape=='image:' then row.gridy=row.gridy+6 end row.dx=0 row.dy=0 local legendShape= getshapetable(row,shape) div:wikitext(legendShape) :tag( 'div' ) :css('position', 'absolute') :css('top',(legendY[lct] or 0)..'px') :css('left', (maxWidth+6)..'px') :css('width', (w-maxWidth-6)..'px') :css('text-align', 'left') :css('line-height','103%') :css('color', getColor(legend.Color)) :css('font-size','10px') :wikitext(legendLine[lct]) :done() end div:allDone() table.insert(result,tostring(div)) end local function getQset (qValue,pValue,argNumber,args) local result={} local sortType=string.lower(args['geo-list-sort'] or 'a') local testValue='0' if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then testValue = mw.wikibase.getLabel(qValue) elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil'))) else if mw.wikibase.getBestStatements(qValue,pValue)[1] then testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0' if pValue == 'P2046' then testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit) end end end elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then if mw.wikibase.getBestStatements(qValue,'P625')[1] then testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0 end elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then if mw.wikibase.getBestStatements(qValue,'P625')[1] then testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0 end end result[1]=testValue result[2]=argNumber return result end function p._main ( args ) local result={} local frame=mw.getCurrentFrame() local dotTable={} local magVal,scaleVal = args.magnify,'' local origH,origW=maplist.height,maplist.width if magVal then --set up the values needed to magnify the top-right portion of the map magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1 if magVal>1 and magVal <=2 then maplist.height= round(maplist.height/magVal,0) maplist.width=round(maplist.width/magVal,0) scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);' else magVal=1 end end -- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">') elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">') else table.insert(result,'<div class="thumb tright">') end table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">') if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end if magVal and magVal >1 and magVal<=2 then table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;') table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ') table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">') else magVal=1 table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;') table.insert(result,'top: 0px; right: 0px; ') table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">') end --add any boundary Qvalues to map-data-light if requested if args['geo-region'] and (args['geo-boundaries'] == '1') then local qmaps=args['map-data-light'] or '' if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end local geoRegion = args['geo-region'] -- or add base-page here local geoSubRegion = args['geo-sub-region'] or 'P150' if not hasWikibase then geoSubRegion = 'P150' elseif not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end if hasWikibase and mw.wikibase.isValidEntityId(geoRegion) then local ct=1 local qtemp while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do -- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id) qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil if qtemp then if mw.wikibase.entityExists(qtemp) then qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp)) end end ct=ct+1 end end if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma args['map-data-light'] = string.sub(qmaps,1,#qmaps-1) end end -- Create the basemap using mapframe local mapframecontent=getmapframecontent(args,'basemap') table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height), zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } ) --Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;') table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>') --Add replacent hover-links for OpenStreetMap and maps terms and conditions table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">') table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>') table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;') table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">') table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>') -- Add scale-line if not args.scalemark or args.scalemark~='0' then local top=maplist.height-42 local left=maplist.width-61-(tonumber(args.scalemark or '1')) local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60') if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end end if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal) local magReduce='' table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111") table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ") table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales if magVal==1 then table.insert(result,"; top:"..tostring(top-1).."px") table.insert(result,"; left:"..tostring(left+16).."px") table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")") else -- shrink the scalemark to compensate for magnification table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px") table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px") table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")") magReduce= 'scale('..tostring(1/magVal)..')' end table.insert(result,"\"></div>") table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;') table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>') end --Set up the shapeList and dotList tables, to provide data to go on the map local sgNumbers,sgSortable={},{} --s1,s2 sgNumbers["1"]="1" if args.useFormatStyle and args.useFormatStyle=='shortstyle' then shapeList=ParseShapeTypes (shapeList,args,"1") for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args local x=string.match(argindex,"[%d]+") -- find its number if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found end if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end end for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end table.sort(sgSortable,lessthan) -- put the list in a sortable form for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards shapeList=ParseShapeTypes (shapeList,args,v) end if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end end local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{} if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then local dotmax=0 local qset={} for indx,val in pairs(args) do if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+"))) if string.match(indx,'geo%-data[%d]+') then local argnum=string.sub(indx,9,#indx) or '' if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then -- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum]) else qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args) end end end if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end end local qlist=args['geo-data-list'] if qlist then if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end else qlist='' end local geoRegion = args['geo-region'] -- or add base-page here local geoSubRegion = args['geo-sub-region'] or 'P150' if not hasWikibase then geoSubRegion = 'P150' elseif not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end if hasWikibase and (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then local ct=1 local qtemp while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do -- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id) qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil if qtemp then qlist=qlist..qtemp..',' -- add subregion Qvalue to the list end ct=ct+1 end end if qlist and #qlist>1 then -- create a table from the csv list local _, c = qlist:gsub(",","") local qtab=splitItem(qlist,c) for xd,yd in pairs(qtab) do yd=string.upper(yd) if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then dotmax=dotmax+1 qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args) args['geo-data'..tostring(dotmax)] = yd -- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found')) elseif not mw.wikibase.isValidEntityId(yd) then debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' )) else debugmsg(mw.addWarning(yd..' is duplicate')) end end end -- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc local ts = {} -- table to hold the keys local useQset=false for ky, val in pairs(qset) do table.insert(ts, {key = ky, value = val[1], num=val[2]}) useQset=true end if useQset then local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then table.sort(ts, function (a,b) return lessthan(a.value, b.value) end) else table.sort(ts, function (a,b) return morethan(a.value, b.value) end) end for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources. end end dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax) for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used local x=tostring(argindex) if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then sgNumbers[x]=x -- add it to the list end end else for indx in pairs(args) do if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList end table.sort(dotList,morethan) for indx,dotName in pairs(dotList) do dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable end end for arcVal = 65,91 do -- check through args looking for any arcs local arcLetter=string.char(arcVal) if args['arc-text'..arcLetter] or args['arc'..arcLetter] then makeArcText(args,result,arcLetter) end end local dotdivs='' local ddots=0 if dotItemTable[1] then local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0) end local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots ) local FullscreenList={} local addcomma='' for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text local dotgroup= dotitem.group or "0" if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines for n,v in pairs(dotItemTable) do if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end end end local qtype=dotitem.group -- find which shape group each dot has been assigned --debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape) if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then table.insert(result, getshapetable(dotitem,shapeList['H'])) else table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot end if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText local ftext='' if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma ) addcomma=', ' end -- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor) -- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end end if args.legendBox then makeLegendBox(result,args) end if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end -- add tag link and details for fullscreen version addcomma='' if (mapframecontent or '[]') ~= '[]' then addcomma=',' end mapframecontent=getmapframecontent(args,'fullscreen') local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket local contentend=' ] } ]' table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">') table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">') table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1', latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip text='<div title="'..fullscreenlinktext..'"> </div>'} } ) table.insert(result,'</div></div>') -- end of maplink ----- -- add closing div for main map table.insert(result,'</div>') -- collate caption material to go in the outer div class if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values local autocaption=string.lower(args['auto-caption'] or 'no') local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer) if negativeAnswer[autoOff] then captionTable='' end if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'<div class="thumbcaption" style="text-align:left">') if args.caption then table.insert(result,args.caption) if captionTable ~= '' then table.insert(result,'<hr>') end end end local columns=tonumber(autoOff:match("[%d]+") or '1') if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons --for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end local capchk=nil local captionList = {} for key, value in pairs(dotItemTable) do -- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then table.insert(captionList, {key = key, value = value}) if captionTable == '' then capchk=true else -- add the next line of content to the html table capchk=false end end end if captionTable ~= '' then table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end) for k,v in pairs(captionList) do local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag local ngrp=v.value.group if captionTableText[ngrp] then -- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')') local c1,c2 c1,c2=checkColors(shapeList[ngrp].shapeColor) local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;' cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>' captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4]) end end end if capchk and (not negativeAnswer[autoOff]) then table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end) -- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "") if string.find(autocaption,'collaps') then table.insert(result,'<div class = "wikitable mw-collapsible ') if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>') end if string.find(autocaption, 'columns=') then columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') ) end table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">') table.sort(dotList,lessthan) local nval,ngrp='','0' for k,v in pairs(captionList) do nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag ngrp=v.value.group or '0' if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then local c1,c2 if nval==args.highlight then c1,c2=checkColors(shapeList['H'].shapeColor) else c1,c2=checkColors(shapeList[ngrp].shapeColor) end table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;') table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>') end end table.insert(result,'</div>') -- end for caption-content div if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame end if captionTable ~= '' then table.insert(result,captionTable..'</table>') end if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame table.insert(result,'</div></div>') -- outer two frames if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box' local w="<small>Below are some template hints for the 'sga' compressed version, " w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. ' w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. ' previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>') w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | ' w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>' w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>" w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>' w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>' w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. ' previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>') if #pmsg then local dbg={} for i,x in pairs(pmsg) do table.insert(dbg,x..'<br>') end dbg=mw.addWarning(table.concat(dbg)) table.insert(result, dbg) end end if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end for i,x in pairs(msg) do table.insert(result, x..'<br>') end return table.concat(result) end function p.main(frame) local args = getArgs(frame) local itemTab={} maplist.width=tonumber(args.width) or 400 maplist.height=tonumber(args.height) or 300 if args.coord then itemTab=splitItem(convertCoordsTrad (args.coord),2) maplist.latbase=itemTab[1] maplist.lonbase=itemTab[2] else maplist.lonbase=tonumber(args.lon) or 5 maplist.latbase=tonumber(args.lat) or 0 end maplist.zoom=tonumber(args.zoom) or 1 visibleLinks=args.showlinks highlightNum=args.highlight if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested return p._main(args) end return p
Summary:
Please note that all contributions to Cultopedia may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Cultopedia:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:OSM Location map/doc
(
edit
)
Search
Search
Editing
Module:OSM Location map
Add topic