Modul:Location map

Aus Wikivoyage
Zur Navigation springen Zur Suche springen
Template-info.png Dokumentation für das Modul Location map[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Das Modul Location map stellt Funktionen zur Erstellung von Positionskarten bereit.

Funktionen

Lagekarte von Ägypten
Golf von
Suez
Ägypten
Lagekarte der Welt
Kairo
Kairo
Orte in Ägypten
Karte Russlands

Kartenfunktionen

function locMap.locationMap(frame)

Stellt eine Karte mit einem Ort dar. Es können weitere Orte und Objekte eingefügt werden.

function locMap.addLocation(frame)

Ergänzt einen weiteren Ort auf der Karte.

function locMap.addObject(frame)

Fügt ein beliebiges Objekt in die Karte ein.

Dokumentation regionaler Kartendaten

function locMap.getMapValue(frame)

Gibt den Wert eines Parameters einer Kartendefinition aus.

function locMap.getMapValueSet(frame)
Erstellt eine Tabelle mit den Parametern einer Kartendefinition.
Hinweise
local locMap = {}

-- Internationalization

local api = {
    apiLocationMap    = 'locationMap',
    apiAddLocation    = 'addLocation',
    apiAddObject      = 'addObject',
    apiGetMapValue    = 'getMapValue',
    apiGetMapValueSet = 'getMapValueSet',
}

local quickbarMapType = 'relief'
local defaultMarker = 'Reddot.svg'

local errMsgs = {
    anError       = 'Fehler',
    unknownMap    = 'Keine Karte für Region <em>%s</em> vorhanden[[Kategorie:Location Map: Keine Karte vorhanden]]',
    noMapImage    = 'Kein Kartenbild spezifiziert',
    notANumber    = '<span class="error">Keine Länge und/oder Breite</span>[[Kategorie:Location Map: Keine Länge und/oder Breite]]',
    wrongLat      = 'Breite %f liegt außerhalb der Kartenbegrenzungen[[Kategorie:Location Map: Koordinate außerhalb der Begrenzung]]',
    wrongLong     = 'Länge %f liegt außerhalb der Kartenbegrenzungen[[Kategorie:Location Map: Koordinate außerhalb der Begrenzung]]',
    wrongXBorders = 'Fehlende oder falsche horizontale Kartengrenzen',
    wrongYBorders = 'Fehlende oder falsche vertikale Kartengrenzen',
    noObject      = 'Kein Objekt angegeben',
    noXPos        = 'Keine horizontale Lage angegeben',
    noYPos        = 'Keine vertikale Lage angegeben',
    noParam       = 'Kein Parameter für getMapValue angegeben',
    notDefined    = 'Parameter nicht definiert',
}

local mapDocs = {
    tableClass    = 'prettytable',
    name          = 'Name',
    description   = 'Beschreibung',
    projection    = 'Projektion',
    top           = 'oben',
    bottom        = 'unten',
    left          = 'links',
    right         = 'rechts',
    default       = 'Standardkarte',
    relief        = 'Physische Karte',
    quickbar      = 'Standardkarte Quickbar',
    mark          = 'Marker',
    marksize      = 'Markergröße',
    linear        = '[[:w:Plattkarte|Plattkarte]]',
    nonlinear     = 'Nichtlineare Projektion',
}

-- Style aliases

local mapStyles = {
    mitte         = "margin: 0 auto !important",
    center        = "margin: 0 auto !important",
    left          = "clear: left; margin: 0 1em 1em 0; float: left",
    links         = "clear: left; margin: 0 1em 1em 0; float: left",
    right         = "clear: right; margin: 0 0 1em 1em; float: right",
    rechts        = "clear: right; margin: 0 0 1em 1em; float: right",
}

local labelStyles = {
    bold          = "font-weight: bold",
    fett          = "font-weight: bold",
    italic        = "font-style: italic",
    kursiv        = "font-style: italic",
    underline     = "text-decoration: underline",
    letterspacing = "letter-spacing: 0.1em",
    wordspacing   = "word-spacing: 0.5em",
    smallcaps     = "font-variant: small-caps",
    uppercase     = "text-transform: uppercase",
    region        = "font-weight: bold; text-transform: uppercase; color: #646464",
    subregion     = "font-weight: bold; color: #646464",
    waterbody     = "font-weight: bold; font-style: italic; letter-spacing: 0.1em; text-transform: uppercase; color: #2A6DB5",
    mountain      = "font-weight: bold; font-style: italic; letter-spacing: 0.1em; color: #704040",
}

local labelPositions1 = {
    ["1"]         = "left: 0; bottom: msize_px;",
    ["2"]         = "left: msize3_px; bottom: -2px;",
    ["3"]         = "left: msize5_px; top: -2em; height: 4em;",
    right         = "left: msize5_px; top: -2em; height: 4em;",
    rechts        = "left: msize5_px; top: -2em; height: 4em;",
    ["4"]         = "left: msize3_px; top: -2px;",
    ["5"]         = "left: 0; top: msize_px;",
    ["6"]         = "left: -3em; top: msize3_px;",
    bottom        = "left: -3em; top: msize3_px;",
    unten         = "left: -3em; top: msize3_px;",
    ["7"]         = "right: 0; top: msize_px;",
    ["8"]         = "right: msize3_px; top: -2px;",
    ["9"]         = "right: msize5_px; top: -2em; height: 4em;",
    left          = "right: msize5_px; top: -2em; height: 4em;",
    links         = "right: msize5_px; top: -2em; height: 4em;",
    ["10"]        = "right: msize3_px; bottom: -2px;",
    ["11"]        = "right: 0; bottom: msize_px;",
    ["12"]        = "left: -3em; bottom: msize3_px;",
    top           = "left: -3em; bottom: msize3_px;",
    oben          = "left: -3em; bottom: msize3_px;",
    center        = "top: -2em; height: 4em; left: -3em;",
    mitte         = "top: -2em; height: 4em; left: -3em;",
}

local labelPositions2 = {
    ["1"]         = "text-align: left;",
    ["2"]         = "text-align: left;",
    ["3"]         = "text-align: left; height: 4em;",
    right         = "text-align: left; height: 4em;",
    rechts        = "text-align: left; height: 4em;",
    ["4"]         = "text-align: left;",
    ["5"]         = "text-align: left;",
    ["6"]         = "text-align: center;",
    bottom        = "text-align: center;",
    unten         = "text-align: center;",
    ["7"]         = "text-align: right;",
    ["8"]         = "text-align: right;",
    ["9"]         = "text-align: right; height: 4em;",
    left          = "text-align: right; height: 4em;",
    links         = "text-align: right; height: 4em;",
    ["10"]        = "text-align: right;",
    ["11"]        = "text-align: right;",
    ["12"]        = "text-align: center;",
    top           = "text-align: center;",
    oben          = "text-align: center;",
    center        = "text-align: center; height: 4em;",
    mitte         = "text-align: center; height: 4em;",
}

-- Local functions, please do not call them directly

local function split(s)
    local split = mw.text.split(s, ';')
    local result = {}
    for key,value in pairs(split) do
        tr = mw.text.trim(value)
        if tr ~= '' then table.insert(result, tr) end
    end
    return result;
end

local function analyzeLabelStyle(style)
    local split = split(style)
    for key,value in pairs(split) do
        if labelStyles[value] ~= nil then split[key] = labelStyles[value] end
    end
    return table.concat(split, '; ') .. ';'
end

local function analyzeMapStyle(style)
    local split = split(style)
    for key,value in pairs(split) do
        if mapStyles[value] ~= nil then split[key] = mapStyles[value] end
    end
    return table.concat(split, '; ') .. ';'
end

local function setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)
    local lmarksize = math.floor(marksize + 0.5)
    local msize = math.floor((marksize - 1) / 2 + 0.5)
    local msize3 = math.floor((marksize + 2) / 2 + 0.5)
    local msize5 = math.floor((marksize + 4) / 2 + 0.5)
    local halfMarkSize = -msize .. 'px'

    -- Setting a marker
    local sCode = '<div style="position: absolute; border-style: none; padding: 0; overflow: visible; top: '
        .. y*100 .. '%; left: ' .. x*100 .. '%;">'
    if mark ~= 'none' then
        sCode = sCode .. '<div style="position: absolute; padding: 0; top: ' .. halfMarkSize .. '; left: '
        .. halfMarkSize .. '; min-width: ' .. lmarksize .. 'px; min-height: ' .. lmarksize .. 'px;">'
        .. '[[File:' .. mark .. '|' .. lmarksize .. 'x' .. lmarksize
        .. 'px|top|class=noviewer|link=' .. name .. '|' .. name .. ']]'
        .. '</div>'
    end

    -- Adding a label
    if (label ~= '') and (label ~= 'none') then
        sCode = sCode .. '<table style="position: absolute; border: none; margin: 0; background-color: transparent; border-collapse: collapse;'
        if (labelWrap ~= nil) and (labelWrap == 'manual') then
            sCode = sCode .. ' white-space: nowrap; width: 10em !important; '
        else
            sCode = sCode .. ' width: 6em !important; '
        end
        if labelPositions1[labelPosition] ~= nil then
            local posStyles = labelPositions1[labelPosition]
            posStyles = string.gsub(posStyles, 'msize_', msize)
            posStyles = string.gsub(posStyles, 'msize3_', msize3)
            posStyles = string.gsub(posStyles, 'msize5_', msize5)
            sCode = sCode .. posStyles .. '"><tr><td style="border: none; padding: 0; vertical-align: middle; '
                .. labelPositions2[labelPosition]
        else
            -- Automatic Estimation
            if y<=0.5 then
                sCode = sCode .. 'top: ' .. msize3 .. 'px; '
            else
                sCode = sCode .. 'bottom: ' .. msize3 .. 'px; '
            end
            if x<0.25 then
                sCode = sCode .. 'text-align: left; left: ' .. math.floor(3 - 60*x)/10 .. 'em;'
                    .. '"><tr><td style="border: none; padding: 0; vertical-align: middle; text-align: left;'
            else if x<0.75 then
                    sCode = sCode .. 'text-align: center; left: -3em;'
                        .. '"><tr><td style="border: none; padding: 0; vertical-align: middle; text-align: center;'
                else
                    sCode = sCode .. 'text-align: right; right: ' .. math.floor(10*(0.3 - (1 - x) * 6))/10 .. 'em;'
                        .. '"><tr><td style="border: none; padding: 0; vertical-align: middle; text-align: right;'
                end
            end
        end
        if (labelWrap ~= nil) and (labelWrap == 'manual') then
            sCode = sCode .. ' white-space: nowrap; width: 10em !important;'
        else
            sCode = sCode .. ' width: 6em !important;'
        end
        sCode = sCode .. '"><span style="' .. analyzeLabelStyle(labelStyle) .. '">'
            .. label .. '</span></td></tr></table>'
    end
    sCode = sCode .. '</div>'
    
    return sCode
end

local function baseMap(mapImage, description, mapStyle, width, caption, captionStyle,
	captionInnerBorder, captionOuterBorder, x, y, name, label, mark, marksize, labelStyle,
	labelWrap, labelPosition, places)
    
    local sCode = '<table class="locationMap" style="overflow: visible; max-width: none !important; border-collapse: collapse;'
    -- Test if fixed or variable width (..x..). Force width if fixed and a caption is specified
    if (string.find(width, 'x') == nil) and (caption ~= '') then
        sCode = sCode .. ' width: ' .. tostring(tonumber(width)+10) ..'px !important;'
    else
        sCode = sCode .. ' width: auto !important;'
    end
    if caption ~= '' then sCode = sCode .. ' border: ' .. captionOuterBorder .. ';' end
    sCode = sCode .. ' ' .. analyzeMapStyle(mapStyle) .. '"><tr><td class="thumb" style="width: auto; overflow: visible; border: none; padding: '
    if caption ~= '' then sCode = sCode .. '3px 3px 0;">' else sCode = sCode .. '0;">' end
    sCode = sCode .. '<div class="noresize" style="overflow: visible; max-width: none !important; padding: 0; margin: 0 auto !important; position: relative; border: '
    if caption ~= '' then sCode = sCode .. captionInnerBorder .. ';' else sCode = sCode .. 'none;' end
    -- Test if fixed or variable width (..x..). Force width if fixed
    if string.find(width, 'x') == nil then sCode = sCode .. ' width: ' .. width ..'px !important;">' else sCode = sCode .. ' width: auto !important;">' end
    sCode = sCode .. '[[File:' .. mapImage .. '|' .. width .. 'px|center|class=noviewer|' .. description .. ']]'
    if (x<0) or (x>1) or (y<0) or (y>1) then
        sCode = sCode .. '<div style="position: absolute; width: 50%; top: 50%; left: 25%; color: #ff0000; font-weight: bold; text-align: center;">'
           .. 'Fehlerhafte Koordinate ' .. name .. '</div>'
    else
        sCode = sCode .. setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)
    end
    sCode = sCode .. places .. '</div></td></tr>'
    if caption ~= '' then
        sCode = sCode .. '<tr><td class="thumbcaption"'
        if captionStyle ~= '' then
            sCode = sCode .. ' style="'.. captionStyle .. '"'
        end
        sCode = sCode .. '>' .. caption .. '</td></tr>'
    end
    sCode = sCode .. '</table>'
    return sCode
end

-- Handling regional map data

local function getMapData(id)
    local region = require('Modul:Location map data ' .. id)
    if (region ~= nil) and (region.data ~= nil) then
        region.data.id = id
        return region.data
    else
        return nil
    end
end

-- This function is never to be called directly but with a pcall()
-- to handle exceptions in case of missing map modules
local function getMapObject(id)
    local region = require('Modul:Location map data ' .. id)
    if (region ~= nil) and (region.data ~= nil) then
        region.data.id = id
        return region
    else
        return nil
    end
end

local function linearX(data, long)
    local left = data.left
    local right = data.right
    if (data == nil) or (left == nil) or (right == nil) or  (left == right) then
        -- Error
        return -1
    else if left < right then
            return (long - left) / (right - left)
        else
            if long < 0 then
                return (360 + long - left) / (360 + right - left)
            else
                return (long - left) / (360 + right - left)
            end
        end
    end
end

local function linearY(data, lat)
    local top = data.top
    local bottom = data.bottom
    if (data == nil) or (top == nil) or (bottom == nil) or  (top == bottom) then
        -- Error
        return -1
    else
        return (lat - top) / (bottom - top)
    end
end

local function getX(anObject, long, lat)
    if anObject.x ~= nil then
        return anObject.x(lat, long)
    else
    	return linearX(anObject.data, long)
    end
end

local function getY(anObject, long, lat)
    if anObject.y ~= nil then
        return anObject.y(lat, long)
    else
    	return linearY(anObject.data, lat)
    end
end

local function getMapImage(data, which)
    local image = data.default
    local w = which
    if w == 'quickbar' then
        w = quickbarMapType
        if (data.quickbar ~= nil) and (data.quickbar ~= '') then
            w = data.quickbar
        end
    end
    if (w ~= '') and (data[w] ~= nil) and (data[w] ~= '') then
        image = data[w]
    end
    return image
end

-- Parameters and error handling

local function argCheck(param, altValue)
    if not param or param == '' then
        return altValue
    else
        local val = mw.text.trim(param)
        if val == '' then val = altValue end
        return val
    end
end

local function errorStr(s)
    return '<strong class="error">Fehler im Modul <em>Location map</em>: ' .. s .. '</strong>'
end

-- Map functions

local function apiLocationMap(args)
    local map = argCheck(args.map, 'missing')
    local success, mObject = pcall(getMapObject, map)
    if not success then
        return errorStr(string.format(errMsgs.unknownMap, map))
    else
        -- Error handling
        local errorMsgs = {}
        success = true
        
        -- Parameters check
        local mData = mObject.data
        local description = ''
        if mData.description ~= nil then description = mData.description end

        if not args.lat or not args.long then
        	return errMsgs.notANumber
        end
        local lat = tonumber(argCheck(tostring(args.lat), 0))
        local long = tonumber(argCheck(tostring(args.long), 0))
        local x = getX(mObject, long, lat)
        if (x<0) or (x>1) then
            success = false
            if x == -1 then
                table.insert(errorMsgs, errorStr(errMsgs.wrongXBorders))
            else
                table.insert(errorMsgs, errorStr(string.format(errMsgs.wrongLong, long)))
            end
        end
        local y = getY(mObject, long, lat)
        if (y<0) or (y>1) then
            success = false
            if y == -1 then
                table.insert(errorMsgs, errorStr(errMsgs.wrongYBorders))
            else
                table.insert(errorMsgs, errorStr(string.format(errMsgs.wrongLat, lat)))
            end
        end

        local maptype = argCheck(args.maptype, 'default')
        local mapImage = argCheck(args.alternativeMap, getMapImage(mData, maptype))
        if (mapImage == nil) or (mapImage == '') then
            success = false
            table.insert(errorMsgs, errorStr(errMsgs.noMapImage))
        end
        if not success then
            return table.concat(errorMsgs, '<br />')
        else
            local name = argCheck(args.name, '')
            local label = argCheck(args.label, '')
            -- Checking width syntax
            local width = argCheck(tostring(args.width), '')
            if (string.match(width, '^%d+$') == nil) and (string.match(width, '^%d*x%d+$') == nil) then
                width = '200x200'
            end
            if mData.mark ~= nil then mark = mData.mark else mark = defaultMarker end
            mark = argCheck(args.mark, mark)
            if mData.marksize ~= nil then marksize = mData.marksize else marksize = 5 end
            local marksize = argCheck(args.marksize, marksize)
            local mapStyle = argCheck(args.mapStyle, 'mitte')
            local labelStyle = argCheck(args.labelStyle, '')
            local labelBackground = argCheck(args.labelBackground, '')
            if labelBackground ~='' then
                labelBackground = 'background: ' .. labelBackground
                if labelStyle ~='' then
                    labelStyle = labelStyle .. '; ' .. labelBackground
                else
                    labelStyle = labelBackground
                end
            end
            local labelWrap = argCheck(args.labelWrap, 'auto')
            local labelPosition = argCheck(args.labelPosition, 'auto')
            local caption = argCheck(args.caption, '')
            local captionStyle = argCheck(args.captionStyle, '')
            local captionInnerBorder = argCheck(args.captionInnerBorder, '1px solid #cccccc')
            local captionOuterBorder = argCheck(args.captionOuterBorder, '1px solid #cccccc')
            local places = argCheck(args.places, '')

            return baseMap(mapImage, description, mapStyle, width, caption, captionStyle,
            	captionInnerBorder, captionOuterBorder, x, y, name, label, mark, marksize,
            	labelStyle, labelWrap, labelPosition, places)
        end
    end
end

local function apiAddLocation(args)
    local map = argCheck(args.map, 'missing')
    local success, mObject = pcall(getMapObject, map)
    if not success then
        return errorStr(string.format(errMsgs.unknownMap, map))
    else
        -- Error handling
        local errorMsgs = {}
        success = true

        -- Parameters check
        local mData = mObject.data
        local lat = tonumber(argCheck(tostring(args.lat), 0))
        local long = tonumber(argCheck(tostring(args.long), 0))
        local x = getX(mObject, long, lat)
        if (x<0) or (x>1) then
            success = false
            if x == -1 then
                table.insert(errorMsgs, errorStr(errMsgs.wrongXBorders))
            else
                table.insert(errorMsgs, errorStr(string.format(errMsgs.wrongLong, long)))
            end
        end
        local y = getY(mObject, long, lat)
        if (y<0) or (y>1) then
            success = false
            if y == -1 then
                table.insert(errorMsgs, errorStr(errMsgs.wrongYBorders))
            else
                table.insert(errorMsgs, errorStr(string.format(errMsgs.wrongLat, lat)))
            end
        end
        if not success then
            return table.concat(errorMsgs, '<br />')
        else
            local name = argCheck(args.name, '')
            local label = argCheck(args.label, '')

            if mData.mark ~= nil then mark = mData.mark else mark = defaultMarker end
            mark = argCheck(args.mark, mark)
            if mData.marksize ~= nil then marksize = mData.marksize else marksize = 5 end
            local marksize = argCheck(args.marksize, marksize)
            local mapStyle = argCheck(args.mapStyle, 'mitte')
            local labelStyle = argCheck(args.labelStyle, '')
            local labelBackground = argCheck(args.labelBackground, '')
            if labelBackground ~='' then
                labelBackground = 'background: ' .. labelBackground
                if labelStyle ~='' then
                    labelStyle = labelStyle .. '; ' .. labelBackground
                else
                    labelStyle = labelBackground
                end
            end
            local labelWrap = argCheck(args.labelWrap, 'auto')
            local labelPosition = argCheck(args.labelPosition, 'auto')

            return setLocation(x, y, name, label, mark, marksize, labelStyle, labelWrap, labelPosition)
        end
    end
end

local function apiAddObject(args)
    local anObject = argCheck(args.object, '')
    if anOject == '' then
        return errorStr(errMsgs.noObject)
    else
        local success = true
        local errorMsgs = {}
        
        local right = argCheck(args.right, '')
        local left = argCheck(args.left, '')
        if (right == '') and (left == '') then
            success = false
            table.insert(errorMsgs, errorStr(errMsgs.noXPos))
        end
        local top = argCheck(args.top, '')
        local bottom = argCheck(args.bottom, '')
        if (top == '') and (bottom == '') then
            success = false
            table.insert(errorMsgs, errorStr(errMsgs.noYPos))
        end
        if not success then
            return table.concat(errorMsgs, '<br />')
        else
            local objectStyle = argCheck(args.objectStyle, '')
            local objectBackground = argCheck(args.objectBackground, '')
            if objectBackground ~='' then
                objectBackground = 'background: ' .. objectBackground
                if objectStyle ~='' then
                    objectStyle = objectStyle .. '; ' .. objectBackground
                else
                    objectStyle = objectBackground
                end
            end
            sCode = '<table style="border-collapse: collapse; margin: 0; padding: 0; width: auto !important; overflow: visible; position: absolute; background: transparent; '
            if (left ~='') then
                sCode = sCode .. 'left: ' .. left .. '; '
            else
                sCode = sCode .. 'right: ' .. right .. '; '
            end
            if (top ~='') then
                sCode = sCode .. 'top: ' .. top .. ';"><tr><td style="border: none; padding: 0; '
            else
                sCode = sCode .. 'bottom: ' .. bottom .. ';"><tr><td style="border: none; padding: 0; '
            end
            sCode = sCode .. analyzeLabelStyle(objectStyle)
                .. '">' .. anObject .. '</td></tr></table>'

            return sCode
        end
    end
end

-- Documentation of map data

local function apiGetMapValue(args)
    local map = argCheck(args.map, 'missing')
    local success, mData = pcall(getMapData, map)
    if not success then
        return errorStr(string.format(errMsgs.unknownMap, map))
    else
        local param = argCheck(args.param, '')
        if param == '' then
            return errorStr(errMsgs.noParam)
        else
            if mData[param] == nil then
                return errMsgs.anError
            else
                return mData[param]
            end
        end
    end
end

local function apiGetMapValueSet(args)
    local map = argCheck(args.map, 'missing')
    local success, mData = pcall(getMapData, map)
    if not success then
        return errorStr(string.format(errMsgs.unknownMap, map))
    else
        local paramList = {'name', 'description', 'projection', 'top', 'bottom',
           'left', 'right', 'default', 'relief', 'quickbar', 'mark', 'marksize'}
        local sCode = '<table class="' .. mapDocs.tableClass .. '">'
        for i = 1, #paramList, 1 do
            sCode = sCode .. '<tr><th style="text-align: left">' .. mapDocs[paramList[i]] .. '</th><td>'
            if mData[paramList[i]] == nil then
                sCode = sCode .. errMsgs.notDefined .. '</td></tr>'
            else
                local v = mData[paramList[i]]
                if mapDocs[v] ~= nil then v = mapDocs[v] end
            	sCode = sCode .. v .. '</td></tr>'
            end
        end
        return sCode .. '</table>'
    end
end

-- API function calls

locMap[api.apiLocationMap] = function (frame)
    return apiLocationMap(frame.args)
end

locMap[api.apiAddLocation] = function (frame)
    return apiAddLocation(frame.args)
end

locMap[api.apiAddObject] = function (frame)
    return apiAddObject(frame.args)
end

locMap[api.apiGetMapValue] = function (frame)
    return apiGetMapValue(frame.args)
end

locMap[api.apiGetMapValueSet] = function (frame)
    return apiGetMapValueSet(frame.args)
end

-- Example for usage in a Lua script

function locMap.exampleLuaCall()
    local frame = {}
    frame.args = {
        map   = 'de',
        lat   = 52.51789,
        long  = 13.38873,
        name  = 'Berlin',
        label = '[[Berlin]]',
    }
    return locMap.locationMap(frame)
end

return locMap