Hoppa till innehållet

Modul:OSM

Från Plutten

Dokumentationen för denna modul kan skapas på Modul:OSM/dok

p={}
require("Modul:Referenshantering")
require("Modul:Debug")
-- Variables to help localisation (i.e. just copy the rest of the code and change here if you want to use the module on another wikipedia language version)
localisation=localisation or {}
localisation.txtCategory='Kategori'

localisation.pipes=localisation.pipes or {}
localisation.pipes.all='allt'
localisation.pipes.ancient='fornminnen'
localisation.pipes.energy='energi'
localisation.pipes.transport='transport'
localisation.pipes.history='historia'
localisation.pipes.education='utbildning'
localisation.pipes.wateractivity='vattenaktivitet'

localisation.pipes.abbeys='kloster'
localisation.pipes.airports='flygplatser'
localisation.pipes.amusementparks='nöjesparker'
localisation.pipes.ancientgraves='forngravar'
localisation.pipes.archaeologicalfinds='arkeologiska fynd'
localisation.pipes.battles='slag'
localisation.pipes.beaches='stränder'
localisation.pipes.bogs='mossar'
localisation.pipes.bridges='broar'
localisation.pipes.castles='herresäten'
localisation.pipes.churches='kyrkor'
localisation.pipes.combustion='förbränning'
localisation.pipes.companies='företag'
localisation.pipes.glacialerratic='flyttblock'
localisation.pipes.harbours='hamnar'
localisation.pipes.hospitals='sjukhus'
localisation.pipes.hotels='hotell'
localisation.pipes.hydropower='vattenkraft'
localisation.pipes.lighthouses='fyrar'
localisation.pipes.mines='gruvor'
localisation.pipes.museums='museum'
localisation.pipes.nuclearpower='kärnkraft'
localisation.pipes.nursinghomes='vårdhem'
localisation.pipes.peaks='höjder'
localisation.pipes.populatedplaces='bebodda platser'
localisation.pipes.quarries='stenbrott'
localisation.pipes.restaurants='restauranger'
localisation.pipes.schools='skolor'
localisation.pipes.seniorhomes='äldreboenden'
localisation.pipes.settlements_localdefinition='tätorter'
localisation.pipes.sieges='belägringar'
localisation.pipes.solarpower='solkraft'
localisation.pipes.sportsvenues='sport'
localisation.pipes.springs='källor'
localisation.pipes.stations='stationer'
localisation.pipes.theatres='teatrar'
localisation.pipes.towns='städer'
localisation.pipes.trade='handel'
localisation.pipes.universities='universitet'
localisation.pipes.villages='byar'
localisation.pipes.wastewater='reningsverk'
localisation.pipes.waterfalls='vattenfall'
localisation.pipes.waterworks='vattenverk'
localisation.pipes.watermills='vattenkvarnar'
localisation.pipes.windpower='vindkraft'

localisation.pipes.ferryroutes='färjeleder'
localisation.pipes.railroads='järnvägar'
localisation.pipes.roads='vägar'
localisation.pipes.watercourses='vattendrag'

localisation.pipes.lakes='sjöar'
localisation.pipes.naturereserves='naturreservat'

localisation.arguments=localisation.arguments or  {}
localisation.arguments.longitude="longitud"
localisation.arguments.latitude="latitud"
localisation.arguments.width="bredd"
localisation.arguments.height="höjd"

localisation.arguments.area="area"
localisation.arguments.connectingline="järnvägslinje"
localisation.arguments.diocese='stift'
localisation.arguments.mountainrange='bergskedja'
localisation.arguments.partof="delav"
localisation.arguments.place="plats"
localisation.arguments.water="vatten"

localisation.maintenance=localisation.maintenance or {}
localisation.maintenance.allalternatives='Artiklar som visar OSM-karta med alla alternativ'
localisation.maintenance.noname='Namn inte inlagt'
p.iCounter=0
p.allowedTypes = {}
p.allowedTypesLines={}
p.allowedTypesShapes={}

p.icon_instances={}
p.icon_symbols={}
p.icon_colours={}

p.line_instances={}
p.line_widths={}
p.line_colours={}
 
p.shape_instances={}
p.shape_widths={}
p.shape_opacities={}
p.shape_colours={}

local txtBaseTitle="BIND(CONCAT('[[', COALESCE(SUBSTR(STR(?article), 31),'" .. localisation.maintenance.noname .. "'), '|', COALESCE(?idLabel,'" .. localisation.maintenance.noname .. "'), ']]') AS ?title)"
local txtOptionalAddition=""
local txtBaseQueryIcons = [=[SELECT DISTINCT ?geo ?id ?title (%s) as ?marker_symbol) (%s) as ?marker_color) ('small' as ?marker_size) WHERE {?id p:P31 ?typeStatement. ?typeStatement ps:P31 ?type. ?id wdt:P625 ?geo; wdt:%s ?qids. VALUES ?qids { %s } %s  %s VALUES ?allowedTypes { %s } FILTER (?type IN (?allowedTypes)) OPTIONAL { ?article schema:about ?id; schema:inLanguage 'sv'; schema:isPartOf <https://sv.wikipedia.org/>. } %s SERVICE wikibase:label { bd:serviceParam wikibase:language '%s'. ?id rdfs:label ?idLabel. ?type rdfs:label ?typeLabel.  } %s } ORDER BY ?type]=]
local txtBaseQueryLines = [=[SELECT DISTINCT ?id ?title  (%s) as ?line_width) (%s) as ?line_color) WHERE {?id p:P31 ?typeStatement. ?typeStatement ps:P31 ?type. ?id wdt:%s ?qids. VALUES ?qids { %s } %s %s VALUES ?allowedTypes { %s } FILTER (?type IN (?allowedTypes)) OPTIONAL { ?article schema:about ?id; schema:inLanguage 'sv'; schema:isPartOf <https://sv.wikipedia.org/>. } %s SERVICE wikibase:label { bd:serviceParam wikibase:language '%s'. ?id rdfs:label ?idLabel. ?type rdfs:label ?typeLabel.  } %s } ORDER BY ?type]=]
local txtBaseQueryShapes = [=[SELECT DISTINCT ?id ?title  (%s) as ?line_width) (%s) as ?fill_color) (%s) as ?fill_opacity) WHERE {?id p:P31 ?typeStatement. ?typeStatement ps:P31 ?type. ?id wdt:%s ?qids. VALUES ?qids { %s } %s %s VALUES ?allowedTypes { %s } FILTER (?type IN (?allowedTypes)) OPTIONAL { ?article schema:about ?id; schema:inLanguage 'sv'; schema:isPartOf <https://sv.wikipedia.org/>. } %s SERVICE wikibase:label { bd:serviceParam wikibase:language '%s'. ?id rdfs:label ?idLabel. ?type rdfs:label ?typeLabel.  } %s } ORDER BY ?type]=]

local txtBaseActiveOnly = "FILTER NOT EXISTS { ?id wdt:P576 [] }  FILTER NOT EXISTS { ?type pq:P582 [] } FILTER NOT EXISTS { ?typeStatement pq:P582 [] }"
local txtBaseActive = "FILTER NOT EXISTS { ?id wdt:P576 [] }"
local txtBaseInactive = "?id wdt:P576 []"

kartpositionmall = {
	Ryssland = 'Ryssland3',
	Sverige  = 'Sverige3', 
	USA      = 'USA2'
}

-- Define a "class" (a table with methods and a constructor)
local Colour = {}
Colour.__index = Colour

-- Constructor function
function Colour:new(red,green,blue)
    local instance = setmetatable({}, Colour)
    -- Ensure values are within the valid range (0-255)
    instance.red = math.max(0, math.min(255, red))
    instance.green = math.max(0, math.min(255, green))
    instance.blue = math.max(0, math.min(255, blue))
    return instance
end

-- Add a fade method to the Colour class
function Colour:fade(fadeFactor)
    -- Ensure fadeFactor is between 0 and 1
    fadeFactor = math.min(1, math.max(0, fadeFactor or 0.7))

    -- Calculate faded RGB values
    local fadedR = math.floor(self.red + (255 - self.red) * fadeFactor)
    local fadedG = math.floor(self.green + (255 - self.green) * fadeFactor)
    local fadedB = math.floor(self.blue + (255 - self.blue) * fadeFactor)

    -- Create and return a new Colour object with the faded values
    return Colour:new(fadedR, fadedG, fadedB)
end

-- Method
function Colour:html()
    -- Convert each component to a two-digit hexadecimal string
    local hexRed = string.format("%02x", self.red)
    local hexGreen = string.format("%02x", self.green)
    local hexBlue = string.format("%02x", self.blue)
    -- Concatenate and return the result
    return "#" .. hexRed .. hexGreen .. hexBlue
end

_black=Colour:new(0, 0, 0)
_white=Colour:new(255, 255, 255)
_red=Colour:new(128,0,0)
_green=Colour:new(0,128,0)
_grey=Colour:new(12,128,128)
_blue=Colour:new(0, 0, 128)
_gold=Colour:new(128, 128, 0)
_brown=Colour:new(139, 69, 19)  --"#8B4513"
_strongred=Colour:new(170, 0, 0)
_strongblue=Colour:new(0, 0, 170)
_educationcolour=Colour:new(69, 19, 139)   -- "#45138B"
_historicalcolour=Colour:new(139, 69, 19)  --"#8B4513"
_roadcolour=_historicalcolour -- Colour:new(240, 194, 121)  --"#F0C279"
_sportscolour=Colour:new(232, 122,23)  --"#E87A17"
_economycolour=Colour:new(240, 194, 121)  
_beachcolour=Colour:new(210, 170, 120)    -- #D2AA78
_lighthousecolour=Colour:new(127, 179, 224) --#7FB3E0 
_mountaincolour=_historicalcolour --#8B4513
colourInverseFill=Colour:new(221, 221, 221) --'#dddddd'
colourInverseStroke=Colour:new(34, 34, 34) --'#222222'
_basincolour=Colour:new(255, 255, 0) -- #FFFF00
boggreen=Colour:new(138, 154, 91) -- #8A9A5B 

-- Non-language settings
settings=settings or {}
settings.width=250
settings.height=250
settings.paths={}
settings.paths.width={}
settings.paths.width.main=5
settings.paths.width.tributary=1
settings.paths.colour={}
settings.paths.colour.rivers=_blue
settings.areas={}
settings.areas.width=1
settings.areas.lake_opacity=0.5
settings.pipes={}
settings.pipes.settlement_localdefinition='Q12813115'
settings.languages="mul, sv, da, no, en"

-- p.addSurroundings
-- Fills a map with icons based on sparql-queries (created by the function listed under FUNCTIONS TO ADD VARIOUS ICONS/SHADED AREA TO MAPS below). The sparql query will find objects that has a relevant p.pid (like P206 for bodies of water) set to qid. 
-- The icons it returns is determined by the content of args
--
-- Arguments:
-- qid [string]      id of the relevant wikidata object
-- args [array]      what will be shown on the map (the function will check the content of this array against )

p.addSurroundings=function(qids,args)
	local checkconditions=true
	local conditionNo=1
	p.filter=''
	while args['condition' .. conditionNo] do
		p.addFilter(args["condition" .. conditionNo])
		conditionNo=conditionNo+1
	end
	if (bHasArgument(args,'ancient') or bHasArgument(args,localisation.pipes.ancient)) then			-- Adds ancient monuments
		p.addIcons('ancient')
		end
	if (bHasArgument(args,'education') or bHasArgument(args,localisation.pipes.education)) then			-- Adds educational institutions
		p.addIcons('schools')
		p.addIcons('universities')
		end
	if (bHasArgument(args,'energy') or bHasArgument(args,localisation.pipes.energy)) then			-- Adds all type of energy extraction units
		p.addIcons('hydropower')
		p.addIcons('nuclearpower')
		p.addIcons('solarpower')
		p.addIcons('windpower')
		p.addIcons('combustion')
	end
	if (bHasArgument(args,'history') or bHasArgument(args,localisation.pipes.history)) then
		p.addIcons('abbeys')
		p.addIcons('battles')
		p.addIcons('castles')
		p.addIcons('churches')
		end
	if (bHasArgument(args,'transport') or bHasArgument(args,localisation.pipes.transport)) then			-- Adds airports, ports, railway stations
		-- points
		p.addIcons('airports')
		p.addIcons('harbours')
		p.addIcons('stations')
		p.addIcons('bridges')
		-- lines
		p.addLines('roads')
		p.addLines('railroads')
		p.addLines('ferryroutes')
		end
	if (bHasArgument(args,'wateractivity') or bHasArgument(args,localisation.pipes.wateractivity)) then		-- Add hydropower plants
		p.addIcons('hydropower')
		p.addIcons('beaches')
		p.addIcons('lighthouses')
		p.addIcons('waterfalls')
		p.addIcons('watermills')
		p.addIcons('wastewater')
		p.addIcons('waterworks')
--		p.addWatercourses()
	end
	
	if (bHasArgument(args,'abbeys') or bHasArgument(args,localisation.pipes.abbeys)) then
		p.addIcons('abbeys')
		end
	if (bHasArgument(args,'ancientgraves') or bHasArgument(args,localisation.pipes.ancientgraves)) then			-- Adds ancient graves
		p.addIcons('ancientgraves')
		end
	if (bHasArgument(args,'airports') or bHasArgument(args,localisation.pipes.airports)) then
		p.addIcons('airports')
		end
	if (bHasArgument(args,'amusementparks') or bHasArgument(args,localisation.pipes.amusementparks)) then
		p.addIcons('amusementparks')
	end
	if (bHasArgument(args,'archaeologicalfinds') or bHasArgument(args,localisation.pipes.archaeologicalfinds)) then
		p.addIcons('archaeologicalfinds')
	end
	if (bHasArgument(args,'battles')  or bHasArgument(args,localisation.pipes.battles) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then		-- Adds battles
		p.addIcons('battles')
		end
	if (bHasArgument(args,'beaches')  or bHasArgument(args,localisation.pipes.beaches) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds beaches
		p.addIcons('beaches')
		end
	if (bHasArgument(args,'bogs')  or bHasArgument(args,localisation.pipes.bogs)) then			-- Adds bogs
		p.addShapes('bogs')
		end
	if (bHasArgument(args,'bridges')  or bHasArgument(args,localisation.pipes.bridges) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('bridges')
		end
	if (bHasArgument(args,'castles')  or bHasArgument(args,localisation.pipes.castles) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then		-- Adds castles
		p.addIcons('castles')
		end
	if (bHasArgument(args,'churches') or bHasArgument(args,localisation.pipes.churches) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('churches')
		end
	if (bHasArgument(args,'combustion') or bHasArgument(args,localisation.pipes.combustion)) then
		p.addIcons('combustion')
		end
	if (bHasArgument(args,'companies') or bHasArgument(args,localisation.pipes.companies) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('companies')
		end
	if (bHasArgument(args,'ferryroutes')  or bHasArgument(args,localisation.pipes.ferryroutes)) then
		p.addLines('ferryroutes')
		end
	if (bHasArgument(args,'glacialerratic') or bHasArgument(args,localisation.pipes.glacialerratic)) then		-- Adds quarries
		p.addIcons('glacialerratic')
		end
	if (bHasArgument(args,'harbours')  or bHasArgument(args,localisation.pipes.harbours) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('harbours')
		end
	if (bHasArgument(args,'hospitals') or bHasArgument(args,localisation.pipes.hospitals) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds hospitals
		p.addIcons('hospitals')
		end
	if (bHasArgument(args,'hotels') or bHasArgument(args,localisation.pipes.hotels)) then		
		p.addIcons('hotels')
		end		
	if (bHasArgument(args,'hydropower') or bHasArgument(args,localisation.pipes.hydropower) or bHasArgument(args,'all')  or bHasArgument(args,localisation.pipes.all)) then		-- Add hydropower plants
		p.addIcons('hydropower')
		end
	if (bHasArgument(args,'lakes') or bHasArgument(args,localisation.pipes.lakes)) then
		p.addAreas('lakes')
		end
	if (bHasArgument(args,'lighthouses')  or bHasArgument(args,localisation.pipes.lighthouses)) then
		p.addIcons('lighthouses')
		end
	if (bHasArgument(args,'mines') or bHasArgument(args,localisation.pipes.mines)) then		-- Adds mines
		p.addIcons('mines')
		end
	if (bHasArgument(args,'museums') or bHasArgument(args,localisation.pipes.museums) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds educational institutions
		p.addIcons('museums')
		end
	if (bHasArgument(args,'naturereserves') or bHasArgument(args,localisation.pipes.naturereserves) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addAreas('naturereserves')
		end
	if (bHasArgument(args,'nursinghomes') or bHasArgument(args,localisation.pipes.nursinghomes)) then			-- Adds hospitals
		p.addIcons('nursinghomes')
		end
	if (bHasArgument(args,'peaks') or bHasArgument(args,localisation.pipes.peaks)) then
		p.addIcons('peaks')
		end
	if (bHasArgument(args,'populatedplaces') or bHasArgument(args,localisation.pipes.populatedplaces)) then
		p.addIcons('populatedplaces')
		end
	if (bHasArgument(args,'quarries') or bHasArgument(args,localisation.pipes.quarries)) then		-- Adds quarries
		p.addIcons('quarries')
		end
	if (bHasArgument(args,'railroads')  or bHasArgument(args,localisation.pipes.railroads)) then
		p.addLines('railroads')
		end
	if (bHasArgument(args,'restaurants')  or bHasArgument(args,localisation.pipes.restaurants)) then
		p.addIcons('restaurants')
		end
	if (bHasArgument(args,'roads')  or bHasArgument(args,localisation.pipes.roads)) then
		p.addLines('roads')
		end
	if (bHasArgument(args,'schools') or bHasArgument(args,localisation.pipes.schools) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds educational institutions
		p.addIcons('schools')
		end
	if (bHasArgument(args,'sieges') or bHasArgument(args,localisation.pipes.sieges)) then			-- Adds sieges
		p.addIcons('sieges')
		end
	if (bHasArgument(args,'seniorhomes') or bHasArgument(args,localisation.pipes.seniorhomes)) then			-- Adds senior homes
		p.addIcons('seniorhomes')
		end
	if (bHasArgument(args,localisation.pipes.settlements_localdefinition)) then
		p.addIcons('settlements_localdefinition')
		end
	if (bHasArgument(args,'solarpower') or bHasArgument(args,localisation.pipes.solarpower)) then
		p.addIcons('solarpower')
	end
	if (bHasArgument(args,'sportsvenues')  or bHasArgument(args,localisation.pipes.sportsvenues) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds sports venue
		p.addIcons('sportsvenues')
		end
	if (bHasArgument(args,'springs')  or bHasArgument(args,localisation.pipes.springs)) then
		p.addIcons('springs')
		end
	if (bHasArgument(args,'stations')  or bHasArgument(args,localisation.pipes.stations) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('stations')
		end
	if (bHasArgument(args,'theatres') or bHasArgument(args,localisation.pipes.theatres) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then			-- Adds hospitals
		p.addIcons('theatres')
		end
	if (bHasArgument(args,'towns')  or bHasArgument(args,localisation.pipes.towns) or bHasArgument(args,'settlements') or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('towns')
		end
	if (bHasArgument(args,'trade') or bHasArgument(args,localisation.pipes.trade) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('trade')
		end
	if (bHasArgument(args,'universities') or bHasArgument(args,localisation.pipes.universities)) then			-- Adds educational institutions
		p.addIcons('universities')
		end
	if (bHasArgument(args,'villages')  or bHasArgument(args,localisation.pipes.villages) or bHasArgument(args,'settlements') or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then
		p.addIcons('villages')
		end
	if (bHasArgument(args,'wastewater') or bHasArgument(args,localisation.pipes.wastewater)) then		
		p.addIcons('wastewater')
		end		
	if (bHasArgument(args,'watercourses') or bHasArgument(args,localisation.pipes.watercourses)) then		
		p.addLines('watercourses')
		end		
	if (bHasArgument(args,'waterfalls') or bHasArgument(args,localisation.pipes.waterfalls)) then			-- Adds water fall 
		p.addIcons('waterfalls')
		end
	if (bHasArgument(args,'watermills') or bHasArgument(args,localisation.pipes.watermills) or bHasArgument(args,'all') or bHasArgument(args,localisation.pipes.all)) then		-- Adds watermills
		p.addIcons('watermills')
		end
	if (bHasArgument(args,'waterworks') or bHasArgument(args,localisation.pipes.waterworks)) then		
		p.addIcons('waterworks')
		end		
	if (bHasArgument(args,'windpower') or bHasArgument(args,localisation.pipes.windpower)) then		-- Add hydropower plants
		p.addIcons('windpower')
		end
	local qidValues = {}
	for qid in pairs(qids) do
	    table.insert(qidValues, string.format("wd:%s", qid))
	end
	p.qidValuesStr = table.concat(qidValues, " ")
	p.onlyActive=args["onlyactive"];
	local txtContent=p.buildSparqlQueryIcons() .. p.buildSparqlQueryShapes()
	if (not bHasArgument(args,'all')) or (not bHasArgument(args,localisation.pipes.showlines)) then
		txtContent=txtContent .. p.buildSparqlQueryLines()
	end
	return txtContent
end

-- p.addQueriedLocations
-- Creates a sparql-query that returns point objects (like cities, battles etc.). The sparql-query is then used to create a mapframe block.  
--
-- Arguments:
-- qid [string]				id of the relevant wikidata object
-- iInstanceOf [string]     what the returned items should be an instance of (so for example Q159719 for power stations)
-- txtIcon [string]		    icon shown (see https://www.mediawiki.org/wiki/Help:Extension:Kartographer/Icons for available icons)
-- txtIconSize [number]		icon size (usually small)
-- txtIconColour [string]   icon rgb colour

p.addQueriedLocations=function (iInstanceOf,txtIcon,txtIconSize,colourIcon)
   table.insert(p.allowedTypes, "wd:" .. iInstanceOf)
   table.insert(p.icon_instances,iInstanceOf)
   table.insert(p.icon_symbols,txtIcon)
   table.insert(p.icon_colours,colourIcon)
end

--p.addQueriedLines
-- Create a sparql-query that returns line objects (like water courses). The sparql-query is then used to create a mapframe block.
--
-- Arguments:
-- qid [string]             id of the relevant wikidata object
-- iInstanceOf [string]     what the returned items should be an instance of (so for example Q159719 for power stations)
-- iWidth [number]		    width of border in pixels
-- txColour  [string]		icon colour

p.addQueriedLines=function (iInstanceOf,iWidth,colour)
   table.insert(p.allowedTypesLines, "wd:" .. iInstanceOf)
   table.insert(p.line_instances,iInstanceOf)
   table.insert(p.line_widths,iWidth)
   table.insert(p.line_colours,colour)
end

--p.addQueriedAreas
-- Create a sparql-query that returns area objects (like countries or nature reserves). The sparql-query is then used to create a mapframe block.
--
-- Arguments:
-- qid [string]             id of the relevant wikidata object
-- iInstanceOf [string]     what the returned items should be an instance of (so for example Q159719 for power stations)
-- iWidth [number]		    width of border in pixels
-- txColour  [string]		icon colour
p.addQueriedAreas=function (iInstanceOf,iWidth,colour,fOpacity)
   table.insert(p.allowedTypesShapes, "wd:" .. iInstanceOf)
   table.insert(p.shape_instances,iInstanceOf)
   table.insert(p.shape_widths,iWidth)
   table.insert(p.shape_colours,colour)
   table.insert(p.shape_opacities,fOpacity)

--   table.insert(p.widthShapes, string.format("?type = wd:%s, '%s'", iInstanceOf, iWidth))
--   table.insert(p.colourShapes, string.format("?type = wd:%s, '%s'", iInstanceOf, colour:html()))
--  table.insert(p.opacityShapes, string.format("?type = wd:%s, %s", iInstanceOf, fOpacity))
end

local function mergeTables(table1, table2, formatString, methodChain)
    local mergedTable = {}
    for i = 1, math.min(#table1, #table2) do
        local arg1 = table1[i]
        local arg2 = table2[i]

        -- Apply the method chain to arg2 (e.g., arg2:fade(0.5):html())
        if methodChain then
            for method in methodChain:gmatch("[^:]+") do
                -- Extract method name and arguments (if any)
                local methodName, argsStr = method:match("^([%a%_]+)(%b())$")
                methodName = methodName or method  -- No arguments

                -- Parse arguments manually (e.g., "0.5" from "fade(0.5)")
                local args = {}
                if argsStr then
                    -- Remove parentheses and split by commas
                    local argsContent = argsStr:sub(2, -2)
                    for arg in argsContent:gmatch("[^%,%s]+") do
                        -- Convert to number if possible, otherwise keep as string
                        local num = tonumber(arg)
                        table.insert(args, num or arg)
                    end
                end

                -- Call the method on arg2
                if type(arg2[methodName]) == "function" then
                    if #args > 0 then
                        arg2 = arg2[methodName](arg2, unpack(args))
                    else
                        arg2 = arg2[methodName](arg2)
                    end
                end
            end
        end

        -- Insert the formatted string into the merged table
        table.insert(mergedTable, string.format(formatString, arg1, arg2))
    end
    return mergedTable
end

local function buildNestedIfLogic(conditions, defaultValue)
    -- Start with the last condition and the default value
    local nestedIf = string.format("if (%s, '%s'", conditions[#conditions], defaultValue)
    -- For each preceding condition, nest it as the "else" part of the next
    for i = #conditions - 1, 1, -1 do
        nestedIf = string.format("if(%s, %s)", conditions[i], nestedIf)
    end
    return nestedIf
end

p.buildSparqlQueryIcons=function()
    if p.icon_instances and (not (next(p.icon_instances) == nil)) then
	    local allowedTypesStr = table.concat(p.allowedTypes, " ")
		local symbolLogic = mergeTables(
		    p.icon_instances,
		    p.icon_symbols,   
		    "?type = wd:%s, '%s'"  -- Format string
		)
		local colourLogicActive = mergeTables(
		    p.icon_instances,    
		    p.icon_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "html"           -- Method chain: call :html()
		)
		local colourLogicInactive = mergeTables(
		    p.icon_instances,    
		    p.icon_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "fade():html"           -- Method chain: call :html()
		)

	    local symbolLogicStr =  buildNestedIfLogic(symbolLogic, "square")
	    local colourLogicActiveStr =  buildNestedIfLogic(colourLogicActive, "#000000")
	    local colourLogicInactiveStr =  buildNestedIfLogic(colourLogicInactive, "#000000")

		if (p.onlyActive) then
		    local txtQuery = string.format(txtBaseQueryIcons, symbolLogicStr, colourLogicActiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseActiveOnly, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geopoint")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
			else
		    local txtQuery = string.format(txtBaseQueryIcons, symbolLogicStr, colourLogicActiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseActive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    local txtReturn=p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geopoint")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    txtQuery = string.format(txtBaseQueryIcons, symbolLogicStr, colourLogicInactiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseInactive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    txtReturn=txtReturn .. p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geopoint")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    return txtReturn
		    end
	    else
	return ""
	end
end

p.buildSparqlQueryLines=function()
    if p.line_instances and (not (next(p.line_instances) == nil)) then
        local allowedTypesStr = table.concat(p.allowedTypesLines, " ")

		local widthLines = mergeTables(
		    p.line_instances,
		    p.line_widths,   
		    "?type = wd:%s, %s"  -- Format string
		)
		
		local colourLogicActive = mergeTables(
		    p.line_instances,    
		    p.line_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "html"           -- Method chain: call :html()
		)
		local colourLogicInactive = mergeTables(
		    p.line_instances,    
		    p.line_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "fade():html"           -- Method chain: call :html()
		)

	    local widthStr =  buildNestedIfLogic(widthLines, 1)
	    local colourLogicActiveStr =  buildNestedIfLogic(colourLogicActive, "#000000")
	    local colourLogicInactiveStr =  buildNestedIfLogic(colourLogicInactive, "#000000")

		if (p.onlyActive) then
		    local txtQuery = string.format(txtBaseQueryLines, widthStr, colourLogicActiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseActiveOnly, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoline")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
			else
		    local txtQuery = string.format(txtBaseQueryLines, widthStr, colourLogicActiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseActive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    local txtReturn=p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoline")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    txtQuery = string.format(txtBaseQueryLines, widthStr, colourLogicInactiveStr, p.pid, p.qidValuesStr, p.filter, txtBaseInactive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    txtReturn=txtReturn .. p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoline")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    return txtReturn
		    end
    else
    	return ""
	end
end

p.buildSparqlQueryShapes=function()
    if p.shape_instances and (not (next(p.shape_instances) == nil)) then
	    local allowedTypesStr = table.concat(p.allowedTypesShapes, " ")

		local widthShapes = mergeTables(
		    p.shape_instances,
		    p.shape_widths,   
		    "?type = wd:%s, %s"  -- Format string
		)
		
		local colourLogicActive = mergeTables(
		    p.shape_instances,    
		    p.shape_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "html"           -- Method chain: call :html()
		)
		local colourLogicInactive = mergeTables(
		    p.shape_instances,    
		    p.shape_colours,   
		    "?type = wd:%s, '%s'",  -- Format string
		    "fade():html"           -- Method chain: call :html()
		)

		local opacityShapes = mergeTables(
		    p.shape_instances,
		    p.shape_opacities,   
		    "?type = wd:%s, %s"  -- Format string
		)

	    local widthStr =  buildNestedIfLogic(widthShapes, "1")
	    local colourLogicActiveStr =  buildNestedIfLogic(colourLogicActive, "#000000")
	    local colourLogicInactiveStr =  buildNestedIfLogic(colourLogicInactive, "#000000")
	    local opacityStr =  buildNestedIfLogic(opacityShapes, 1)

		if (p.onlyActive) then
		    local txtQuery = string.format(txtBaseQueryShapes, widthStr, colourLogicActiveStr, opacityStr, p.pid, p.qidValuesStr, p.filter, txtBaseActiveOnly, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoshape")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
			else
		    local txtQuery = string.format(txtBaseQueryShapes, widthStr, colourLogicActiveStr, opacityStr, p.pid, p.qidValuesStr, p.filter, txtBaseActive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    local txtReturn=p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoshape")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    txtQuery = string.format(txtBaseQueryShapes, widthStr, colourLogicInactiveStr, opacityStr, p.pid, p.qidValuesStr, p.filter, txtBaseInactive, allowedTypesStr, txtOptionalAddition, settings.languages, txtBaseTitle)    
		    txtReturn=txtReturn .. p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoshape")  .. " ," ..p.addArgument("query",txtQuery)  .. "}"	)
		    return txtReturn
	    end
    else
    	return ""
	end
end

p.addIconWD=function(qid,colour,txtIcon,txtTitle,txtSitelink)
  if (txtTitle) then
	if txtSitelink then
  		txtTitle="[["..txtSitelink.."|"..txtTitle.."]]"
		end
	return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geopoint")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("title",txtTitle)  .. " ," .. p.addArgument("marker-color",colour:html())  .. " ," .. p.addArgument("marker-symbol",txtIcon)  .. " ," .. p.addArgument("marker-size","small") .. "} }"	)
	else
	return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geopoint")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("marker-color",colour:html())  .. " ," .. p.addArgument("marker-symbol",txtIcon)  .. " ," .. p.addArgument("marker-size","small") .. "} }"	)
	end
end


-- p.addLineWD
-- Adds a line for a specific wikidata object (given by qid)
--
-- Arguments:
-- qid [string] 			id of the relevant wikidata object
-- txColour [string]		line colour
-- iWidth [number]		    width of line in pixels
-- txtTitle [string]        title for the line shown on the map
-- txtSitelink [string] 	article on the local wikipedia that the title links to
p.addLineWD=function(qid,colour,iWidth,txtTitle,txtSitelink)
  if (txtTitle) then
	if txtSitelink then
  		txtTitle="[["..txtSitelink.."|"..txtTitle.."]]"
		end
	return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoline")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("title",txtTitle)  .. " ," .. p.addArgument("stroke",colour:html())  .. " ," .. p.addArgumentNoQuote("stroke-width",iWidth) .. "} }"	)
	else
	return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoline")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("stroke",colour:html())  .. " ," .. p.addArgumentNoQuote("stroke-width",iWidth) .. "} }"	)
	end
end

-- p.addAreaWD
-- Adds an area for a specific wikidata object (given by qid)
--
-- Arguments:
-- qid [string] 			id of the relevant wikidata object
-- txColour [string]		fill colour
-- iWidth [number]		    width of border in pixels
-- fFillOpacity [number]    opacity of the area
-- txtTitle [string]        title for the line shown on the map
-- txtSitelink [string] 	article on the local wikipedia that the title links to

p.addAreaWD=function(qid,colour,iWidth,fFillOpacity,txtTitle,txtSitelink)
	if (txtTitle) then
		if (txtSitelink) then
		  	txtTitle="[["..txtSitelink.."|"..txtTitle.."]]"
			end
		return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoshape")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("title",txtTitle)  .. " ," .. p.addArgument("stroke",colour:html())  .. " ," .. p.addArgumentNoQuote("stroke-width",iWidth)  .. " ," .. p.addArgumentNoQuote("fill-opacity",fFillOpacity).. "} }")	
		else
		return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geoshape")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("stroke",colour:html())  .. " ," .. p.addArgumentNoQuote("stroke-width",iWidth)  .. " ," .. p.addArgumentNoQuote("fill-opacity",fFillOpacity).. "} }")	
		end
end

-- p.addAreaCommons
-- Adds an area from a specific commons file (given by filename)
--
-- txtFilename [string]		filename (without "Data:")
-- txColour [string]		fill colour
-- iWidth [string]		    width of border in pixels
-- fOpacity [string]     	opacity of the area

p.addAreaCommons=function(txtFilename)
	return p.addMapContent("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","page")  .. " ," ..p.addArgument("title",txtFilename) .. "}")
end

p.addAreaCommonsId=function(qid)
	local txtFullFilename=readProperty(qid,"P3896")
	if (txtFullFilename) then
		local txtFilename=string.sub(txtFullFilename,6)
		if txtFilename then
			return p.addAreaCommons(txtFilename)
		end
	end
	return ""
end


p.addFilter=function(condition)
	local txtRelation,txtProperty,txtValue = getComparisonOperator(condition)
	if (txtRelation) then
		p.filter=p.filter.."?id wdt:" .. txtProperty .. " ?" .. txtProperty .. " . " ..	"FILTER(?" .. txtProperty .." " .. txtRelation .. " " .. txtValue .. ")"
		end
end

-- Check for comparison characters
function getComparisonOperator(str)
    local found = {}
    local positions = {}
    -- Check for each character and record their positions
    for char in pairs({[">"] = true, ["<"] = true, ["="] = true}) do
        local pos = str:find(char, 1, true) -- plain search (no regex)
        if pos then
            table.insert(found, char)
            table.insert(positions, pos)
        end
    end

    -- Return nil if not exactly one operator is found
    if #found ~= 1 then
        return nil, nil, nil
    end

    local op = found[1]
    local pos = positions[1]
    local before = str:sub(1, pos - 1)
    local after = str:sub(pos + 1)

    return op, before, after
end

-- Function to find rows where column `col` has value `value`
local function findRowsByColumn(matrix, col, value)
    local result = {}
    for i, row in ipairs(matrix) do
        if row[col] == value then
--            table.insert(result, i) -- Store the row index
            table.insert(result, row) -- Store the row index
        end
    end
    if not(isempty(result)) then
	    return result
		else
		return false
		end
end

local function getPropertyType(pid)
    local property = mw.wikibase.getEntity(pid)
    if property then
        return property.datatype
    else
        return nil
    end
end

local function propertyToSparql(pid, propertyType)
    local varVal = "?_val_" .. pid
    local varLabel = "?_label_" .. pid
    local varFormatted = "?_formatted_" .. pid

    -- SPARQL to bind the raw value and label
    local optionalClause = string.format([[
        OPTIONAL { ?item wdt:P%s %s. }
        OPTIONAL { %s rdfs:label %s. FILTER(LANG(%s) = "en") }
    ]], pid, varVal, varVal, varLabel, varLabel)

    -- SPARQL to format the value based on its type
    local bindClause
    if propertyType == "time" then
        bindClause = string.format([[
            BIND(
                COALESCE(
                    (IF(BOUND(%s),
                        CONCAT(STRYEAR(%s), "-", STRMONTH(%s), "-", STRDAY(%s)),
                        NULL
                    )),
                    (STR(%s)),
                    "?"
                ) AS %s
            )
        ]], varVal, varVal, varVal, varVal, varVal, varFormatted)
    elseif propertyType == "quantity" then
        bindClause = string.format([[
            BIND(
                COALESCE(
                    (IF(BOUND(%s),
                        CONCAT(STR(%s), " ", %s),
                        NULL
                    )),
                    (STR(%s)),
                    "?"
                ) AS %s
            )
        ]], varVal, varVal, varLabel, varVal, varFormatted)
    elseif propertyType == "wikibase-item" then
        bindClause = string.format([[
            BIND(
                COALESCE(
                    (STR(%s)),
                    (STR(%s)),
                    "?"
                ) AS %s
            )
        ]], varLabel, varVal, varFormatted)
    else -- string, url, etc.
        bindClause = string.format([[
            BIND(
                COALESCE(
                    (STR(%s)),
                    "?"
                ) AS %s
            )
        ]], varVal, varFormatted)
    end

    return optionalClause, bindClause
end


local function moreinfoToSparql(moreinfo)
    local optionalClauses = {}
    local pids = {}  -- Array to store unique PIDs
    local placeholders = {}  -- Map PIDs to their placeholders

    -- Replace P\d+ with placeholders and collect PIDs
    local processedString = moreinfo:gsub("P%d+", function(pid)
        local placeholder = "%%P" .. pid .. "%%"
        table.insert(pids, pid)
        placeholders[pid] = placeholder
        table.insert(optionalClauses, string.format(
            "OPTIONAL { ?id wdt:%s ?val_%s. }", pid, pid
        ))
        return placeholder
    end)

    -- Split the string into parts separated by placeholders
    local parts = {}
    local currentPos = 1
    for _, pid in ipairs(pids) do
        local placeholder = placeholders[pid]
        local startPos, endPos = processedString:find(placeholder, currentPos, true)
        if startPos then
            -- Add the text before the placeholder
            table.insert(parts, processedString:sub(currentPos, startPos - 1))
            -- Add the placeholder as a property reference
            table.insert(parts, { type = "property", pid = pid })
            currentPos = endPos + 1
        end
    end
    -- Add the remaining text after the last placeholder
    table.insert(parts, processedString:sub(currentPos))

    -- Build the CONCAT expression
    local concatParts = {}
    for _, part in ipairs(parts) do
        if type(part) == "table" then
            local pid = part.pid
            local propertyType = getPropertyType(pid)
            if propertyType == "time" then
----	            table.insert(concatParts, string.format("(FORMAT(?val_%s, 'd MMMM yyyy'))", part.pid))
--	            table.insert(concatParts, string.format("(STR(YEAR(?val_%s))),'-',(STR(MONTH(?val_%s))),'-',(STR(DAY(?val_%s)))", part.pid, part.pid, part.pid))

			table.insert(optionalClauses, string.format("BIND(MONTH(?val_%s) AS ?m) BIND(  IF(?m = 1, 'januari', IF(?m = 2, 'februari', IF(?m = 3, 'mars', IF(?m = 4, 'april', IF(?m = 5, 'maj',  IF(?m = 6, 'juni',  IF(?m = 7, 'juli', IF(?m = 8, 'augusti',  IF(?m = 9, 'september', IF(?m = 10, 'oktober', IF(?m = 11, 'november', 'december'))))))))))) AS ?manad)", part.pid))
			table.insert(concatParts, string.format("STR(DAY(?val_%s)), ' ', ?manad, ' ', STR(YEAR(?val_%s))", part.pid, part.pid))
	            else
	            table.insert(concatParts, string.format("(STR(?val_%s))", part.pid))
	            end
            else
			table.insert(concatParts, string.format("'%s'", part:gsub("'", "\\'")))
        end
    end

    local concatExpr = ", ' '," .. table.concat(concatParts, ", ")
    return table.concat(optionalClauses, " "), concatExpr
end

--------------- FUNCTIONS TO ADD VARIOUS ICONS/SHADED AREA TO MAPS----------------------------------------

local arrayIcons={}
table.insert(arrayIcons,{"Q160742","monument","small",_historicalcolour,"abbeys"})
table.insert(arrayIcons,{"Q1701174","monument","small",_historicalcolour:fade(),"abbeys"})
table.insert(arrayIcons,{"Q1248784","airport","small",_black,"airports"})
table.insert(arrayIcons,{"Q194195","amusement-park","small",_educationcolour,"amusementparks"})
table.insert(arrayIcons,{"Q2593777","cemetery","small",_historicalcolour,"ancient"})
table.insert(arrayIcons,{"Q34023","triangle","small",_historicalcolour,"ancient"})
table.insert(arrayIcons,{"Q7321974","landmark-JP","small",_historicalcolour,"ancient"})
table.insert(arrayIcons,{"Q3395377","monument","small",_historicalcolour,"ancient"})
table.insert(arrayIcons,{"Q1426772","monument","small",_historicalcolour,"ancientgraves"})
table.insert(arrayIcons,{"Q10855061","historic","small",_historicalcolour,"archaeologicalfinds"})
table.insert(arrayIcons,{"Q178561","danger","small",_historicalcolour,"battles"})
table.insert(arrayIcons,{"Q1261499","danger","small",_historicalcolour,"battles"})
table.insert(arrayIcons,{"Q567998","beach","small",_beachcolour,"beaches"})
table.insert(arrayIcons,{"Q40080","beach","small",_beachcolour,"beaches"})
table.insert(arrayIcons,{"Q12280","bridge","small",_black,"bridges"})
table.insert(arrayIcons,{"Q23413","castle","small",_historicalcolour,"castles"})
table.insert(arrayIcons,{"Q751876","castle","small",_historicalcolour,"castles"})
table.insert(arrayIcons,{"Q879050","castle","small",_historicalcolour,"castles"})
table.insert(arrayIcons,{"Q17715832","castle","small",_historicalcolour:fade(),"castles"})
table.insert(arrayIcons,{"Q16970","religious-christian","small",_historicalcolour,"churches"})
table.insert(arrayIcons,{"Q17485079","religious-christian","small",_historicalcolour:fade(),"churches"})
table.insert(arrayIcons,{"Q918457","industry","small",_green,"combustion"})
table.insert(arrayIcons,{"Q1601458","industry","small",_green,"combustion"})
table.insert(arrayIcons,{"Q4830453","bank","small",_economycolour,"companies"})
table.insert(arrayIcons,{"Q372934","square","small",_black,"glacialerratic"})
table.insert(arrayIcons,{"Q44782","harbor","small",_black,"harbours"})
table.insert(arrayIcons,{"Q283202","harbor","small",_black,"harbours"})
table.insert(arrayIcons,{"Q16917","hospital","small",_strongred,"hospitals"})
table.insert(arrayIcons,{"Q27686","suitcase","small",_green,"hotels"})
table.insert(arrayIcons,{"Q15911738","dam","small",_strongblue,"hydropower"})
table.insert(arrayIcons,{"Q39715","lighthouse","small",_lighthousecolour,"lighthouses"})
table.insert(arrayIcons,{"Q820477","triangle","small",_black,"mines"})
table.insert(arrayIcons,{"Q33506","museum","small",_educationcolour,"museums"})
table.insert(arrayIcons,{"Q10416961","museum","small",_educationcolour,"museums"})
table.insert(arrayIcons,{"Q134447","building-alt1","small",_black,"nuclearpower"})
table.insert(arrayIcons,{"Q837142","heart","small",_strongred,"nursinghomes"})
table.insert(arrayIcons,{"Q8502","mountain","small",_mountaincolour,"peaks"})
table.insert(arrayIcons,{"Q123964505","home","small",_green,"populatedplaces"})
table.insert(arrayIcons,{"Q15115254","landmark-JP","small",_black,"quarries"})
table.insert(arrayIcons,{"Q11707","restaurant","small",_green,"restaurants"})
table.insert(arrayIcons,{"Q22908","cricket","small",_strongred,"seniorhomes"})
table.insert(arrayIcons,{"Q3914","school","small",_educationcolour,"schools"})
table.insert(arrayIcons,{"Q9842","school","small",_educationcolour,"schools"})
table.insert(arrayIcons,{"Q159334","school","small",_educationcolour,"schools"})
table.insert(arrayIcons,{"Q170087","school","small",_educationcolour,"schools"})
table.insert(arrayIcons,{"Q10511371","school","small",_educationcolour,"schools"})
table.insert(arrayIcons,{settings.pipes.settlement_localdefinition,"home","small",_green,"settlements_localdefinition"})
table.insert(arrayIcons,{"Q188055","gate","small",_historicalcolour,"sieges"})
table.insert(arrayIcons,{"Q285927","star","small",_gold,"solarpower"})
table.insert(arrayIcons,{"Q1003207","star","small",_gold,"solarpower"})
table.insert(arrayIcons,{"Q2298412","star","small",_gold,"solarpower"})
table.insert(arrayIcons,{"Q8524","soccer","small",_sportscolour,"sportsvenues"})
table.insert(arrayIcons,{"Q1076486","soccer","small",_sportscolour,"sportsvenues"})
table.insert(arrayIcons,{"Q1154710","soccer","small",_sportscolour,"sportsvenues"})
table.insert(arrayIcons,{"Q1202618","soccer","small",_sportscolour,"sportsvenues"})
table.insert(arrayIcons,{"Q2338524","car","small",_sportscolour,"sportsvenues"})
table.insert(arrayIcons,{"Q124714","wetland","small",_blue,"springs"})
table.insert(arrayIcons,{"Q55488","rail","small",_black,"stations"})
table.insert(arrayIcons,{"Q55678","rail","small",_black,"stations"})
table.insert(arrayIcons,{"Q336514","theatre","small",_educationcolour,"theatres"})
table.insert(arrayIcons,{"Q515","city","small",_red,"towns"})
table.insert(arrayIcons,{"Q3957","city","small",_red,"towns"})
table.insert(arrayIcons,{"Q216107","bank","small",_green,"trade"})
table.insert(arrayIcons,{"Q288514","bank","small",_green,"trade"})
table.insert(arrayIcons,{"Q3918","college","small",_educationcolour,"universities"})
table.insert(arrayIcons,{"Q532","home","small",_green,"villages"})
table.insert(arrayIcons,{"Q5084","home","small",_green,"villages"})
table.insert(arrayIcons,{"Q486972","home","small",_green,"villages"})
table.insert(arrayIcons,{"Q15242449","recycling","small",_brown,"wastewater"})
table.insert(arrayIcons,{"Q34038","waterfall","small",_blue,"waterfalls"})
table.insert(arrayIcons,{"Q185187","watermill","small",_strongblue,"watermills"})
table.insert(arrayIcons,{"Q10373565","recycling","small",_blue,"waterworks"})
table.insert(arrayIcons,{"Q49833","windmill","small",_lighthousecolour,"windpower"})
table.insert(arrayIcons,{"Q194356","windmill","small",_lighthousecolour,"windpower"})

p.addIcons=function(txtTopic)
	local matchingRows=findRowsByColumn(arrayIcons, 5, txtTopic)
	for _, row in ipairs(matchingRows) do
		p.addQueriedLocations(row[1],row[2],row[3],row[4])
	end
end

local arrayLines={}
table.insert(arrayLines,{"Q34442",3,_roadcolour,"roads"})
table.insert(arrayLines,{"Q909299",3,_roadcolour,"roads"})
table.insert(arrayLines,{"Q728937",3,_black,"railroads"})
table.insert(arrayLines,{"Q18984099",3,_strongblue,"ferryroutes"})
table.insert(arrayLines,{"Q355304",1,_blue,"watercourses"})
table.insert(arrayLines,{"Q63565252",1,_blue,"watercourses"})
table.insert(arrayLines,{"Q4022",1,_blue,"watercourses"})
table.insert(arrayLines,{"Q3529419",1,_blue,"watercourses"})

p.addLines=function(txtTopic)
	local matchingRows=findRowsByColumn(arrayLines, 4, txtTopic)
	for _, row in ipairs(matchingRows) do
		p.addQueriedLines(row[1],row[2],row[3])
	end
end

local arrayAreas={}
table.insert(arrayAreas,{"Q23397",1,_blue,0.2,"lakes"})
table.insert(arrayAreas,{"Q104093746",1,_blue,0.2,"lakes"})
table.insert(arrayAreas,{"Q46169",1,_green,0.3,"naturereserves"})
table.insert(arrayAreas,{"Q179049",1,_green,0.3,"naturereserves"})
table.insert(arrayAreas,{"Q3240227",1,boggreen,0.2,"bogs"})

p.addAreas=function(txtTopic)
	local matchingRows=findRowsByColumn(arrayAreas, 5, txtTopic)
	for _, row in ipairs(matchingRows) do
		p.addQueriedAreas(row[1],row[2],row[3],row[4])
	end
end

p.addArgument=function(txtLabel,txtValue)
	if (txtValue) then return "\"" .. txtLabel .. "\"" .. ":" ..  "\"" .. txtValue .. "\""  end
end

p.addArgumentNoQuote=function(txtLabel,txtValue)
	return "\"" .. txtLabel .. "\"" .. ":" ..   txtValue 
end

-- Function to fill up the mapframe correctly
p.addMapContent=function(txt)
	if (not isempty(txt)) then
		if (p.bNotfirst) then
			p.txtConnect=","
			else
			p.txtConnect=" "
			p.bNotfirst=true
		end
	end
	return p.txtConnect .. txt	
end

-- Creates an inverse mask (so that only items with the area specified by qid is shown)
p.addInverse=function(qid,colourFill,fFillOpacity,colourStroke,fStrokeOpacity,iWidth)
	return p.addMapContent ("{" .. p.addArgument("type","ExternalData")  .. " ," .. p.addArgument("service","geomask")  .. " ," ..p.addArgument("ids",qid)  .. " , \"properties\":{"..p.addArgument("fill",colourFill:html())  .. " ," .. p.addArgumentNoQuote("fill-opacity",fFillOpacity) .. " ," .. p.addArgument("stroke",colourStroke:html())  .. " ," .. p.addArgumentNoQuote("stroke-opacity",fStrokeOpacity) .. " ," .. p.addArgumentNoQuote("stroke-width",iWidth) ..  "} }"	)
end

-- Sets basic mapframe settings (not mandatory for mapframe except for height and width and this function sets default values for them if no value is supplied)
p.showBase=function (frame)
	p.myArgs={}
--	local iHeight, iWidth, bHasMaxLevel, iMaxLevel
	if (not isempty(frame.args['height'])) then
		iHeight=frame.args['height']
		else
		if (not isempty(frame.args[localisation.arguments.height])) then
			iHeight=frame.args[localisation.arguments.height]
			else
			iHeight=settings.height
			end
		end
	if (not isempty(frame.args['width'])) then
		iWidth=frame.args['width']
		else
		if (not isempty(frame.args[localisation.arguments.width])) then
			iWidth=frame.args[localisation.arguments.width]
			else
			iWidth=settings.width
			end
		end
	if (not isempty(frame.args['zoom'])) then
		p.myArgs['zoom']=frame.args['zoom']
	end
	if (not isempty(frame.args['longitude'])) then
		p.myArgs['longitude']=frame.args['longitude']
	end
	if (not isempty(frame.args[localisation.arguments.longitude])) then
		p.myArgs['longitude']=frame.args[localisation.arguments.longitude]
	end
	if (not isempty(frame.args['latitude'])) then
		p.myArgs['latitude']=frame.args['latitude']
	end
	if (not isempty(frame.args[localisation.arguments.latitude])) then
		p.myArgs['latitude']=frame.args[localisation.arguments.latitude]
	end
	if (not isempty(frame.args['align'])) then
		p.myArgs['align']=frame.args['align']
	end
	if (not isempty(frame.args['text'])) then
		p.myArgs['text']=frame.args['text']
	end
	if (bHasArgument(frame.args,"frameless")) then
		p.myArgs['frameless']=""
	end
	
	p.myArgs['height']=iHeight
	p.myArgs['width']=iWidth
	return p.myArgs
end

-- Function to split a string by a delimiter (by chatgpt)
split=function(inputStr, delimiter)
	if not delimiter then error "No delimiter supplied to function split" end
    local result = {}
    if inputStr then
	    for match in (inputStr .. delimiter):gmatch("(.-)" .. delimiter) do
			result[match]=true      
	    end
	    end
    return result
end

-- Function to split a string by a delimiter (by chatgpt)
split2=function(inputStr, delimiter)
	if not delimiter then error "No delimiter supplied to function split2" end
    local result = {}
    if inputStr then
	    for match in (inputStr .. delimiter):gmatch("(.-)" .. delimiter) do
			table.insert(result,match)      
	    end
	    end
    return result
end

p.include=function(qid,bIsinline)
	local claim=mw.wikibase.getBestStatements(qid, "P31" )
	for key,value in pairs(claim) do
		iInstance=read(value,'id')
		local row=findRowsByColumn(arrayIcons, 1, iInstance)
		if (#row)>0 then
			local txtContent=p.addIconWD(qid,row[1][4],row[1][2],getLabelFundamental(qid),mw.wikibase.getSitelink(qid))
			if (txtContent) then
				if (bIsinline) then
					return txtContent
					else
					return p.frame:extensionTag{ name = 'mapframe', content = txtContent, args = p.showBase(p.frame) }
					end
				end
			end
		local row=findRowsByColumn(arrayLines, 1, iInstance)
		if (#row)>0 then
			local txtContent=p.addLineWD(qid,row[3],row[2],getLabelFundamental(qid),mw.wikibase.getSitelink(qid))
			if (txtContent) then
				if (bIsinline) then
					return txtContent
					else
					return p.frame:extensionTag{ name = 'mapframe', content = txtContent, args = p.showBase(p.frame) }
					end
				end
			end
		local row=findRowsByColumn(arrayAreas, 1, iInstance)
		if (#row)>0 then
			local txtContent=p.addAreaWD(qid,row[3],row[2],row[4],getLabelFundamental(qid),mw.wikibase.getSitelink(qid))
			if (txtContent) then
				if (bIsinline) then
					return txtContent
					else
					return p.frame:extensionTag{ name = 'mapframe', content = txtContent, args = p.showBase(p.frame) }
					end
				end
			end
		end
	return ''
end

p.show=function(frame)
	local qid=frame.args['id']
	p.frame=frame
	return p.include(qid,bHasArgument(frame.args,"inline"))
end

function processTable(t)
    local result = {}
    local length = #t

    for i, v in ipairs(t) do
        -- Remove the first '[' (even with leading spaces)
        v = v:gsub("^%s*%[", "")
        -- Remove the last ']' (even with trailing spaces)
        v = v:gsub("%]%s*$", "")
        table.insert(result, v)
    end

    -- Concatenate all elements with a ',' in between
    return table.concat(result, ", ")
end

p.showMerge=function(frame)
local txtContent = "[" .. processTable(frame.args) .. "]"
return frame:extensionTag{ name = 'mapframe', content = txtContent, args = p.showBase(frame) }
end

function bHasArgument(args,txtName)
    -- Check for the presence of txtName among args
    for _, arg in pairs(args) do
        if arg == txtName then
            -- return true if txtName is found
            return true
        end
    end
    return false -- otherwise return false
end

function readProperty(qid,pid)
	statements=mw.wikibase.getBestStatements(qid,pid)
	if not(next(statements)==nil) then
		if not(statements[1]==nil) then
			return statements[1].mainsnak.datavalue.value
			end
		end
	return nil
	end

p.showArea=function (frame)
	p.txtLabel=frame.args['label']
--	if (not(isempty(p.txtLabel))) then
	if (p.txtLabel) then
		txtOptionalAddition,txtTitleAddition=moreinfoToSparql(p.txtLabel)
		txtBaseTitle="BIND(CONCAT(COALESCE(CONCAT('[[',SUBSTR(STR(?article), 31),'|',?idLabel,']]'),?idLabel,'" .. localisation.maintenance.noname .. "')" .. txtTitleAddition .. ") AS ?title)"
		end
	local data=frame.args
	local myArgs=p.showBase(frame)
	local many=false
	local qids={}
	if (not(isempty(data['id']))) then
		if (string.find(data['id'], ",")) then
			qids=split(data['id'],",")
			many=true
			else
			qid=data['id']
			qids[qid]=true
			end
		else
		if (not(isempty(data['state']))) then
			qid=mw.wikibase.getEntityIdForTitle(data['state'])
			qids[qid]=true
			else
			qid=mw.wikibase.getEntityIdForCurrentPage()
			if not qid then return "Modulen behöver ett Wikidata id! Det kan antingen komma automatiskt från artikeln eller anges genom argumentet 'id' vid anrop av mall eller funktion" end
			qids[qid]=true
			end
		end
	loopids=qids -- to avoid messing up the arrays by adding items to an aray that is currently being looped through
	for keyid in pairs(loopids) do
		local territories=useStatement(keyid,"P150")
		if (territories) then
			for key,value in pairs(territories) do
				if (type(value)=="table") and (next(value) ~= nil) then 
					qids[value["id"]]=true
					end
				end
			end
		end

	local property=data["propertysettoobject"]
	if (not(isempty(property))) then
		arrayProperty=split2(property,",")
		p.pid=p.getProperty(arrayProperty)
		else
		p.pid=p.getProperty(data)
	end
	p.iItems=0
	p.frame=frame
	p.txtContent=p.addSurroundings(qids,p.frame.args)
	local include=frame.args['include']
	if (not isempty(include)) then
		if (string.find(include, ",")) then
			includeids=split(include,",")
		    for includeid, _ in pairs(includeids) do
				p.txtContent=p.txtContent .. p.include(includeid,true)
		    	end
			else
			p.txtContent=p.txtContent .. p.include(include,true)
			end
		end
	
	if bHasArgument(p.frame.args,'nobackground') then
		p.txtContent=p.txtContent..p.addAreaWD(qid,_black,0,0,txtTitle) -- Print the area invisibly (so that the mape gets the right size, but that the area doesn't show)
		else
		if bHasArgument(p.frame.args,'commons') then
			txtFilename=string.sub(readProperty(qid,"P3896"),6)
			p.txtContent=p.txtContent..p.addAreaCommons(txtFilename) -- Print the area visibly based on coordinates from Commons
			else
			p.txtContent=p.txtContent..p.addAreaWD(qid,_black,1,0.2,txtTitle) -- Print the area visibly based on coordinates from OpenStreetMap
			end
		end
	if (not isempty(frame.args['commonsinclude'])) then
		if (string.find(frame.args['commonsinclude'], ",")) then
			commonsincludeids=split(frame.args['commonsinclude'],",")
		    for commonsincludeid, _  in pairs(commonsincludeids) do
				p.txtContent=p.txtContent .. p.addAreaCommonsId(commonsincludeid)
		    	end
			else
			p.txtContent=p.txtContent .. p.addAreaCommonsId(frame.args['commonsinclude'])
			end
		end
	if (not isempty(frame.args['commonspages'])) then
		commonspages=split(frame.args['commonspages'],",")
	    for txtCommonsPage, _  in pairs(commonspages) do
			p.txtContent=p.txtContent .. p.addAreaCommons(txtCommonsPage)
	    	end			
		end
	if (not isempty(frame.args['inverse'])) then
		p.txtContent=p.txtContent .. p.addInverse(frame.args['inverse'],colourInverseFill,0.1,colourInverseStroke,0.5,3)
	end
	if (not isempty(p.txtContent)) then
		p.txtContent="["..p.txtContent.."]"
	end
	if (bHasArgument(data,"inline")) then
		return p.txtContent
		else
		return frame:extensionTag{ name = 'mapframe', content = p.txtContent, args = myArgs }
		end
end

p.getProperty=function(arrayTheme)
	if (bHasArgument(arrayTheme,'connectingline') or bHasArgument(arrayTheme,localisation.arguments.connectingline)) then
		return "P81"
		end
	if (bHasArgument(arrayTheme,'area') or bHasArgument(arrayTheme,localisation.arguments.area)) then
		return "P131"
		end
	if (bHasArgument(arrayTheme,'water') or bHasArgument(arrayTheme,localisation.arguments.water)) then
		return "P206"
		end
	if (bHasArgument(arrayTheme,'place') or bHasArgument(arrayTheme,localisation.arguments.place)) then
		return "P276"
		end
	if (bHasArgument(arrayTheme,'partof') or bHasArgument(arrayTheme,localisation.arguments.partof)) then
		return "P361"
		end
	if (bHasArgument(arrayTheme,'diocese') or bHasArgument(arrayTheme,localisation.arguments.diocese)) then
		return "P708"
		end
	if (bHasArgument(arrayTheme,'mountainrange') or bHasArgument(arrayTheme,localisation.arguments.mountainrange)) then
		return "P4553"
	end
	return "P17"							-- default value

end

local iRiver=1
local myArgs={};
local iCounter=1
local denylist={}
local allrivers={}
local allakes={}

local riverdatabase={}
local lakedatabase={}

local denyassource={Q46831=true,Q3958626=true,Q3777462=true,Q2074737=true} 
-- bergskedja, alpine section, alpine group, kommun i Spanien

-- This function fills a structure with data about the river, its tributaries etc.
p.data_river=function(frame)
	-- first check which id(s) are used
	many=false
	-- if an id is set use it
	p.qids={}
	fid=frame.args['id']
	if (not(isempty(fid))) then
		-- if id is set to something that includes an "," (like "Q1,Q2") it is not one id, but many, in that case store it as ids instead
		if (string.find(fid, ",")) then
			p.qids=split2(fid,",")
			many=true
			else
			table.insert(p.qids,fid)
			end
		else
		-- if no id is set use the one for wikidata object connected to the page
		table.insert(p.qids,mw.wikibase.getEntityIdForCurrentPage())
	end
	-- create a deny list with water bodies to ignore (can for example be channels that would otherwise connect the water with lots of water bodies that are not tributaries)
	if (not(isempty(frame.args['deny']))) then
		denylist=split(frame.args['deny'],",")
	end
	-- show its drainage basin (defaults to true, so will be shown unless basin is set to something that is not true)
	if (not(isempty(frame.args['basin']))) then
		bBasin=frame.args['basin'] == "true"
		else
		bBasin=true
	end
	-- show lakes (defaults to true, so will be shown unless lakes is set to something that is not true)
	if (not(isempty(frame.args['lakes']))) then
		bLakes=frame.args['lakes'] == "true"
		else
		bLakes=true
	end
	-- show tributaries (defaults to true, so will be shown unless tributaries is set to something that is not true)
	if (not(isempty(frame.args['tributaries']))) then
		bTributaries=frame.args['tributaries'] == "true"
		else
		bTributaries=true
	end
	if (bTributaries==nil) then
		bTributaries=true		-- Default value
		end
	if (isempty(frame.args['maxlevel'])) then
		bHasMaxLevel=false
		else
		bHasMaxLevel=true
		iMaxLevel=tonumber(frame.args['maxlevel'])
		end
	for _, id in pairs( p.qids ) do
		p.addRiver(id,0,bTributaries)
	end
	return tprint(riverdatabase) .. tprint(lakedatabase) .. tprint(denylist)
end

p.showRiver=function (frame)
	p.pid='P206'						-- Use located in or next to body of water for sparql queries related to the river
	myArgs=p.showBase(frame)			-- fill a map frame
	p.data_river(frame)					-- Retrieve data about the rivers and lake in the water system (to the degree that relevant properties like lakes and tributaries are set)
	p.iItems=0
	local txtContent=''
	-- Look through the database that has been filled by p.data_river
	tmp={}
	for key, valueRiver in pairs( riverdatabase ) do
		-- select what thickness to use for the river 
		if (valueRiver.level==0) then
			iThickness=settings.paths.width.main
			end
		if (valueRiver.level>0) then
			iThickness=settings.paths.width.tributary
			end
		-- draw a line for the river
		txtContent=	txtContent..p.addLineWD(valueRiver.id,settings.paths.colour.rivers,iThickness,valueRiver.name,valueRiver.sitelink)
		-- print out icons based on sparql queries (depending on what arguments have been set)
	end			
	txtContent=	txtContent..p.addSurroundings(allrivers,frame.args)
	if (bLakes) then	-- if showing lakes ..
		for keyLake, valueLake in pairs( lakedatabase ) do -- .. loop through all the lakes
			-- draw an area for the lake
			txtContent=	txtContent..p.addAreaWD(valueLake.id,settings.paths.colour.rivers,settings.areas.width,settings.areas.lake_opacity,valueLake.name,valueLake.sitelink)
			-- print out icons based on sparql queries (depending on what arguments have been set)
		end
	end
	txtContent=	txtContent..p.addSurroundings(allakes,frame.args)
	if (bBasin) then	-- if showing the river basin(s)
		for _, id in ipairs(p.qids) do
			iBasin=readFirstStatementId(id,'P4614')
			if (iBasin) then
				statements=mw.wikibase.getBestStatements(iBasin,'P3896')
				if not(next(statements)==nil) then
					if not(statements[1]==nil) then
						local txtBasin=statements[1].mainsnak.datavalue.value
						local newtxt=txtBasin:gsub("^Data:%s*", "")
						txtContent=txtContent .. p.addAreaCommons(newtxt)
					end
				end
			end
		end
	end
	if (not isempty(frame.args['inverse'])) then
		txtContent=txtContent .. p.addInverse(frame.args['inverse'],colourInverseFill,0.5,colourInverseStroke,0.9,3)
	end
	if (txtContent) then
		txtContent="["..txtContent.."]"
		end
	txtExtraCategory=''
	if (bHasArgument(frame.args,'all') or bHasArgument(frame.args,localisation.pipes.all)) then
		txtExtraCategory='[[' .. localisation.txtCategory .. ':' .. localisation.maintenance.allalternatives .. ']]'
		end
	if (bHasArgument(frame.args,"inline")) then
		return txtContent
		else
		return frame:extensionTag{ name = 'mapframe', content = txtContent, args = myArgs } .. txtExtraCategory -- return the mapframe
		end
end

p.addLake=function(qid,iLevel)
	if (qid and ((not bHasMaxLevel) or (iLevel<=iMaxLevel)) and (not denylist[qid]) and p.isAllowed(qid)) then
		if not allakes[qid] then
			local item={}
			item.id=qid
			item.name=mw.wikibase.getLabel(qid)
			item.sitelink=mw.wikibase.getSitelink(qid)
			table.insert(lakedatabase,item)
			allakes[qid]=true
			local entityTributary=mw.wikibase.getBestStatements(qid, 'P200' )	
			for key, value in pairs( entityTributary ) do
				local qidTributary=read(value,'id')
				if (p.isLake(qidTributary)) then
					p.addLake(qidTributary,iLevel)
					else
					p.addRiver(qidTributary,iLevel+1,true)
					end
			end
			local entityPart=mw.wikibase.getBestStatements(qid, 'P527' )	
			for key, value in pairs( entityPart ) do
				qidPart=read(value,'id')
				if (p.isLake(qidPart)) then
					p.addLake(qidPart,iLevel)
					end
			end
			local entityBorders=mw.wikibase.getBestStatements(qid, 'P47' )	
			for key, value in pairs( entityBorders ) do
				qidBorder=read(value,'id')
				if (p.isLake(qidBorder)) then
					p.addLake(qidBorder,iLevel)
					end
			end
		end
	end
end

p.isAllowed=function(qid)
	local entity=mw.wikibase.getBestStatements(qid, 'P31' )	
	for key, value in pairs( entity ) do
		id=read(value,'id')
		if (denyassource[id]) then
			return false
		end
	end
	return true
end	

p.isLake=function(qid)
	if (not qid) then return false end
	local entity=mw.wikibase.getBestStatements(qid, 'P31' )	
	for key, value in pairs( entity ) do
		id=read(value,'id')
		if (id=='Q23397') or (id=='Q104093746') or (id=='Q17018380') then
			return true
		end
	end
	return false
end

p.isRiver=function(qid)
	if (not qid) then return false end
	local entity=mw.wikibase.getBestStatements(qid, 'P31' )	
	for key, value in pairs( entity ) do
		id=read(value,'id')
		if (id=='Q355304') or (id=='Q4022') or (id=='Q3529419') or (id=='Q47521') then
			-- vattendrag, flod, å, bäck
			return true
		end
	end
	return false
end

p.addRiver=function(qid,iLevel,bTributaries)
	local level=iLevel
	if qid and ((not allrivers[qid]) and ((not bHasMaxLevel) or (iLevel<=iMaxLevel)) and (not denylist[qid])) then
		local item={}
		item.id=qid
		item.level=level
		item.name=mw.wikibase.getLabel(qid)
		item.sitelink=mw.wikibase.getSitelink(qid)
		table.insert(riverdatabase,item)
		allrivers[qid]=true
		if (bTributaries) then
		-- Tributaries directly from the river
			local entityTributary=mw.wikibase.getBestStatements(qid, 'P974' )	
			local id
			for key, value in pairs( entityTributary ) do
				id=read(value,'id')
				if not allrivers[id] then
					tblRiver=p.addRiver(id,level+1,bTributaries)
					end
			end
			-- Tributaries via the river's source(s)
			local entitySources=mw.wikibase.getBestStatements(qid, 'P885' )	
			for keySource, valueSource in pairs( entitySources ) do
				local id=read(valueSource,'id')
				if (bLakes and p.isLake(id)) then
					p.addLake(id,level)
					end
				if (p.isRiver(id)) then
					p.addRiver(id,level+1,bTributaries)
					end
			end
			if (bLakes) then
				-- Tributaries via lakes the river pass by
				local entityLakes=mw.wikibase.getBestStatements(qid, 'P469' )	
				for keyLake, valueLake in pairs( entityLakes ) do
					local iLake=read(valueLake,'id')
					p.addLake(iLake,level)
				end
			end		
		end
		
	end
end




function almostsame(a,b)
	return (a.txtLocalName==b.txtLocalName) and (a.icon==b.icon) and (a.colour==b.colour)
end

p.documentation_icons=function(frame)
	local processedlist={}
	for i = 1, #arrayIcons do
		local row={}
		row.txtEnglishName=arrayIcons[i][5]
		row.txtLocalName=localisation.pipes[row.txtEnglishName] or "ööö"
		row.qid=convertQID(frame,arrayIcons[i][1])
		row.icon=arrayIcons[i][2]
		row.colour=arrayIcons[i][4]:html()
		table.insert(processedlist,row)
		end

    table.sort(processedlist, function(a, b) return 
    	a.txtLocalName < b.txtLocalName  or  
    	(a.txtLocalName == b.txtLocalName and a.icon < b.icon) or
    	(a.txtLocalName == b.txtLocalName and a.icon == b.icon and a.colour < b.colour)
    end)

	local lastactive=1
	processedlist[1].rowspan=1
	processedlist[1].txt=processedlist[1].qid
	for i = 2, #processedlist do
		if (almostsame(processedlist[i],processedlist[lastactive])) then
			processedlist[lastactive].rowspan=i-lastactive+1
			processedlist[lastactive].txt=processedlist[lastactive].txt .. ", " .. processedlist[i].qid
			else
			lastactive=i
			processedlist[lastactive].rowspan=1
			processedlist[lastactive].txt=processedlist[i].qid
			end
		end
	txtReturn={}
	table.insert(txtReturn,"<table class=\"wikitable sortable\"><tr><th>Namn</th><th>Wikidata objekt</th><th>Ikon</th><th>Färg</th></tr>")
	local i=1
	while i <= #processedlist do
		row=processedlist[i]
		table.insert(txtReturn,"<tr rowspan=\"" .. row.rowspan ..  "\">")
		if (row.txtLocalName=="ööö") then
			table.insert(txtReturn,"<td style='background:#FFFF00'>"..row.txtEnglishName .. "</td>")
			else
			table.insert(txtReturn,"<td>"..row.txtLocalName .. "</td>")
			end
		table.insert(txtReturn,"<td>"..row.txt .. "</td>")
		table.insert(txtReturn,"<td>".."[[Fil:Maki7-".. row.icon .. ".svg]]" .. "</td>")
		table.insert(txtReturn,"<td>"..frame:expandTemplate{ title = "Färgbox", args = { row.colour } } .. "</td>")
		table.insert(txtReturn,"</tr>")
		i=i+processedlist[i].rowspan
		end
	table.insert(txtReturn,"</table>")
	return table.concat(txtReturn, "")
end


p.documentation_lines=function(frame)
	local processedlist={}
	for i = 1, #arrayLines do
		local row={}
		row.txtEnglishName=arrayLines[i][4]
		row.txtLocalName=localisation.pipes[row.txtEnglishName] or "ööö"
		row.qid=convertQID(frame,arrayLines[i][1])
		row.width=arrayLines[i][2]
		row.colour=arrayLines[i][3]:html()
		table.insert(processedlist,row)
		end

    table.sort(processedlist, function(a, b) return 
    	a.txtLocalName < b.txtLocalName  or  
    	(a.txtLocalName == b.txtLocalName and a.colour < b.colour) or
    	(a.txtLocalName == b.txtLocalName and a.colour == b.colour and a.width < b.width)
    end)

	local lastactive=1
	processedlist[1].rowspan=1
	processedlist[1].txt=processedlist[1].qid
	for i = 2, #processedlist do
		if (almostsame(processedlist[i],processedlist[lastactive])) then
			processedlist[lastactive].rowspan=i-lastactive+1
			processedlist[lastactive].txt=processedlist[lastactive].txt .. ", " .. processedlist[i].qid
			else
			lastactive=i
			processedlist[lastactive].rowspan=1
			processedlist[lastactive].txt=processedlist[i].qid
			end
		end
	txtReturn={}
	table.insert(txtReturn,"<table class=\"wikitable sortable\"><tr><th>Namn</th><th>Wikidata objekt</th><th>Bredd</th><th>Färg</th></tr>")
	local i=1
	while i <= #processedlist do
		row=processedlist[i]
		table.insert(txtReturn,"<tr rowspan=\"" .. row.rowspan ..  "\">")
		if (row.txtLocalName=="ööö") then
			table.insert(txtReturn,"<td style='background:#FFFF00'>"..row.txtEnglishName .. "</td>")
			else
			table.insert(txtReturn,"<td>"..row.txtLocalName .. "</td>")
			end
		table.insert(txtReturn,"<td>"..row.txt .. "</td>")
		table.insert(txtReturn,"<td>".. row.width .. "</td>")
		table.insert(txtReturn,"<td>"..frame:expandTemplate{ title = "Färgbox", args = { row.colour } } .. "</td>")
		table.insert(txtReturn,"</tr>")
		i=i+processedlist[i].rowspan
		end
	table.insert(txtReturn,"</table>")
	return table.concat(txtReturn, "")
end

--table.insert(arrayAreas,{"Q3240227",1,boggreen,0.2,"bogs"})

p.documentation_shapes=function(frame)
	local processedlist={}
	for i = 1, #arrayAreas do
		local row={}
		row.txtEnglishName=arrayAreas[i][5]
		row.txtLocalName=localisation.pipes[row.txtEnglishName] or "ööö"
		row.qid=convertQID(frame,arrayAreas[i][1])
		row.width=arrayAreas[i][2]
		row.colour=arrayAreas[i][3]:html()
		row.opacity=arrayAreas[i][4]
		table.insert(processedlist,row)
		end

    table.sort(processedlist, function(a, b) return 
    	a.txtLocalName < b.txtLocalName  or  
    	(a.txtLocalName == b.txtLocalName and a.colour < b.colour) or
    	(a.txtLocalName == b.txtLocalName and a.colour == b.colour and a.opacity < b.opacity) or
    	(a.txtLocalName == b.txtLocalName and a.colour == b.colour and a.opacity == b.opacity and a.width < b.width)
    end)

	local lastactive=1
	processedlist[1].rowspan=1
	processedlist[1].txt=processedlist[1].qid
	for i = 2, #processedlist do
		if (almostsame(processedlist[i],processedlist[lastactive])) then
			processedlist[lastactive].rowspan=i-lastactive+1
			processedlist[lastactive].txt=processedlist[lastactive].txt .. ", " .. processedlist[i].qid
			else
			lastactive=i
			processedlist[lastactive].rowspan=1
			processedlist[lastactive].txt=processedlist[i].qid
			end
		end
	txtReturn={}
	table.insert(txtReturn,"<table class=\"wikitable sortable\"><tr><th>Namn</th><th>Wikidata objekt</th><th>Bredd</th><th>Opacitet</th><th>Färg</th></tr>")
	local i=1
	while i <= #processedlist do
		row=processedlist[i]
		table.insert(txtReturn,"<tr rowspan=\"" .. row.rowspan ..  "\">")
		if (row.txtLocalName=="ööö") then
			table.insert(txtReturn,"<td style='background:#FFFF00'>"..row.txtEnglishName .. "</td>")
			else
			table.insert(txtReturn,"<td>"..row.txtLocalName .. "</td>")
			end
		table.insert(txtReturn,"<td>"..row.txt .. "</td>")
		table.insert(txtReturn,"<td>".. row.width .. "</td>")
		table.insert(txtReturn,"<td>".. row.opacity .. "</td>")
		table.insert(txtReturn,"<td>"..frame:expandTemplate{ title = "Färgbox", args = { row.colour } } .. "</td>")
		table.insert(txtReturn,"</tr>")
		i=i+processedlist[i].rowspan
		end
	table.insert(txtReturn,"</table>")
	return table.concat(txtReturn, "")
end

function symbols (arrayNames,arraySymbols,iIdColumn,first,symbol_type,frame)
	local txtReturn=''
	for _, txtArgument in pairs(arrayNames) do
		local txtArrayArgument=txtArgument
		for txtEnglishName, txtLocalName in pairs(localisation.pipes) do
			if (txtLocalName==txtArgument) then
				txtArrayArgument=txtEnglishName
				break
				end
			end
		local matchingRows=findRowsByColumn(arraySymbols, iIdColumn, txtArrayArgument)
		if next(matchingRows) then
			if first then
				first = false
				else
				txtReturn = txtReturn .. ", "
				end
			if symbol_type=="icons" then
				txtReturn=txtReturn.. "[[Fil:Maki7-".. matchingRows[1][2] .. ".svg|16px]] " .. txtArgument
				end
			if symbol_type=="areas" then
				local thiscolour=matchingRows[1][3]
				txtReturn=txtReturn.. frame:expandTemplate{ title = "Färgbox", args = { thiscolour:html() } } .. " " .. txtArgument
				end
			end
	end
	return txtReturn,first
end

function allsymbols(arrayNames,first,frame)
	txtIcons,first=symbols(arrayNames,arrayIcons,5,first,"icons")
	txtAreas,first=symbols(arrayNames,arrayAreas,5,first,"areas",frame)
	return txtIcons .. txtAreas,first
end

p.showSymbols=function(frame)
	local themes=frame.args["themes"]
	local arrayThemes
	if (isempty(themes)) then
		arrayThemes=frame.args
		else
		arrayThemes=split2(themes,",")
	end
	txtArguments,first=allsymbols(arrayThemes,true,frame)
	arrayExtraArguments=split2(frame.args['description_extra'],",")
	txtExtraArguments,first=allsymbols(arrayExtraArguments,first,frame)
	return txtArguments .. txtExtraArguments
end

p.documentation=function(frame)
	if bHasArgument(frame.args,'icons') then
		return p.documentation_icons(frame)
		end
	if bHasArgument(frame.args,'lines') then
		return p.documentation_lines(frame)
		end
	if bHasArgument(frame.args,'shapes') then
		return p.documentation_shapes(frame)
		end
	return ""
end

function convertQID(frame,qid)
    if qid:match("^Q%d+$") then
        local number = qid:sub(2)
        -- Expand the template and return the rendered output
        return frame:expandTemplate{
            title = "Q",
            args = { number }
        }
    else
        return qid
    end
end


return p