Пример #1
0
def l_function_maass_browse_page():
    info = {"bread": get_bread(2, [("Maass Form", url_for('.l_function_maass_browse_page'))])}
    info["contents"] = [processMaassNavigation()]
    info["gl2spectrum0"] = [paintSvgMaass(1, 10, 0, 10, L="/L")]
    info["colorminus1"] = rgbtohex(signtocolour(-1))
    info["colorplus1"] = rgbtohex(signtocolour(1))
    return render_template("MaassformGL2.html", title='L-functions of GL(2) Maass Forms of Weight 0', **info)
Пример #2
0
def browse_graph(min_level, max_level, min_R, max_R):
    r"""
    Render a page with a graph with clickable dots for all
    with min_R <= R <= max_R and levels in the similar range.
    """
    info = {}
    info['contents'] = [paintSvgMaass(min_level, max_level, min_R, max_R)]
    info['min_level'] = min_level
    info['max_level'] = max_level
    info['min_R'] = min_R
    info['max_R'] = max_R
    info['coloreven'] = rgbtohex(signtocolour(1))
    info['colorodd'] = rgbtohex(signtocolour(-1))
    bread = bread_prefix() + [('Browse graph', '')]
    info['bread'] = bread
    info['learnmore'] = learnmore_list()
    return render_template("maass_browse_graph.html", title='Browsing graph of Maass forms', **info)
Пример #3
0
def render_maass_browse_graph(min_level, max_level, min_R, max_R):
    r"""
    Render a page with a graph with clickable dots for all
    with min_R <= R <= max_R and levels in the similar range.
    """
    info = {}
    info['contents'] = [paintSvgMaass(min_level, max_level, min_R, max_R)]
    info['min_level'] = min_level
    info['max_level'] = max_level
    info['min_R'] = min_R
    info['max_R'] = max_R
    info['coloreven'] = rgbtohex(signtocolour(1))
    info['colorodd'] = rgbtohex(signtocolour(-1))
    bread = [('Modular forms', url_for('mf.modular_form_main_page')),
             ('Maass waveforms', url_for('.render_maass_waveforms'))]
    info['bread'] = bread

    return render_template("mwf_browse_graph.html", title='Browsing graph of Maass forms', **info)
Пример #4
0
def render_maass_browse_graph(min_level, max_level, min_R, max_R):
    r"""
    Render a page with a graph with clickable dots for all
    with min_R <= R <= max_R and levels in the similar range.
    """
    info = {}
    info['contents'] = [paintSvgMaass(min_level, max_level, min_R, max_R)]
    info['min_level'] = min_level
    info['max_level'] = max_level
    info['min_R'] = min_R
    info['max_R'] = max_R
    info['coloreven'] = rgbtohex(signtocolour(1))
    info['colorodd'] = rgbtohex(signtocolour(-1))
    bread = [('Modular Forms', url_for('mf.modular_form_main_page')),
             ('Maass Waveforms', url_for('.render_maass_waveforms'))]
    info['bread'] = bread

    return render_template("mwf_browse_graph.html", title='Browsing graph of Maass forms', **info)
Пример #5
0
 def test_signtocolour(self):
     r"""
     Checking utility: signtocolour
     """
     self.assertEqual(signtocolour(0), 'rgb(63,63,255)')
     self.assertEqual(signtocolour(1 + 2j), 'rgb(197,0,184)')
Пример #6
0
 def test_signtocolour(self):
     r"""
     Checking utility: signtocolour
     """
     self.assertEqual(signtocolour(0), 'rgb(63,63,255)')
     self.assertEqual(signtocolour(1+2j), 'rgb(197,0,184)')
Пример #7
0
def paintSvgMaass(min_level, max_level, min_R, max_R, weight=0, char=1,
                  width=1000, heightfactor=20, L=""):
    ''' Returns the contents (as a string) of the svg-file for
        all Maass forms in the database.
        Takes all levels from min_level to max_level
        Spectral parameter in [min_R, max_R] 
        Set L="/L" to make link go to the L-function
    '''
    xMax = int(max_R)
    yMax = int(max_level)
    xMin = int(min_R)
    yMin = int(min_level)
    extraSpace = 40
    length_R = xMax - xMin
    length_level = yMax - yMin + 1
    if length_level < 15:
        heightfactor = heightfactor * 2
    height = length_level * heightfactor + extraSpace
    xfactor = (width - extraSpace)/length_R
    yfactor = (height - extraSpace)/length_level
    ticlength = 4
    radius = 3
    xshift = extraSpace

    # Start of file and add coordinate system
    ans = "<svg  xmlns='http://www.w3.org/2000/svg'"
    ans += " xmlns:xlink='http://www.w3.org/1999/xlink'"
    ans += " height='{0}' width='{1}'>\n".format(height + 20, width + 20)
    ans += paintCSMaass(width, height, xMin, xMax, yMin, yMax,
                        xfactor, yfactor, ticlength, xshift)

    # Fetch Maass forms from database
    # NB although base.getDBConnection().PORT works it gives the
    # default port number of 27017 and not the actual one!
    if pymongo.version_tuple[0] < 3:
        host = base.getDBConnection().host
        port = base.getDBConnection().port
    else:
        host, port = base.getDBConnection().address
    db = MaassDB(host=host, port=port)
    search = {'level1': yMin, 'level2': yMax, 'char': char,
              'R1': xMin, 'R2': xMax, 'Newform' : None, 'weight' : weight}
    fields = {'Eigenvalue', 'Level', 'Symmetry'}
    forms = db.get_Maass_forms(search, fields, 
                               do_sort=False, limit=10000)

    # Loop through all forms and add a clickable dot for each
    for f in forms:
        linkurl = L + "/ModularForm/GL2/Q/Maass/{0}".format(f['_id'])
        x = (f['Eigenvalue'] - xMin) * xfactor + xshift
        y = (f['Level'] - yMin + 1) * yfactor
        try:  # Shifting even slightly up and odd slightly down
            if f['Symmetry'] == 0 or f['Symmetry'] == 'even':
                y -=  1
                color = signtocolour(1)
            elif f['Symmetry'] == 1 or f['Symmetry'] == 'odd':
                y += 1
                color = signtocolour(-1)
            else:
                color = signtocolour(0)
        except Exception:
            color = signtocolour(0)
            
        ans += "<a xlink:href='{0}' target='_top'>".format(linkurl)
        ans += "<circle cx='{0}' cy='{1}' ".format(str(x)[0:6],str(y))
        ans += "r='{0}'  style='fill:{1}'>".format(str(radius),color)
        ans += "<title>{0}</title></circle></a>\n".format(f['Eigenvalue'])

    ans += "</svg>"
    return ans
Пример #8
0
def paintSvgMaass(min_level,
                  max_level,
                  min_R,
                  max_R,
                  weight=0,
                  char=1,
                  width=1000,
                  heightfactor=20,
                  L=""):
    ''' Returns the contents (as a string) of the svg-file for
        all Maass forms in the database.
        Takes all levels from min_level to max_level
        Spectral parameter in [min_R, max_R] 
        Set L="/L" to make link go to the L-function
    '''
    xMax = int(max_R)
    yMax = int(max_level)
    xMin = int(min_R)
    yMin = int(min_level)
    extraSpace = 40
    length_R = xMax - xMin
    length_level = yMax - yMin + 1
    if length_level < 15:
        heightfactor = heightfactor * 2
    height = length_level * heightfactor + extraSpace
    xfactor = (width - extraSpace) / length_R
    yfactor = (height - extraSpace) / length_level
    ticlength = 4
    radius = 3
    xshift = extraSpace

    # Start of file and add coordinate system
    ans = "<svg  xmlns='http://www.w3.org/2000/svg'"
    ans += " xmlns:xlink='http://www.w3.org/1999/xlink'"
    ans += " height='{0}' width='{1}'>\n".format(height + 20, width + 20)
    ans += paintCSMaass(width, height, xMin, xMax, yMin, yMax, xfactor,
                        yfactor, ticlength, xshift)

    # Fetch Maass forms from database
    search = {
        'level1': yMin,
        'level2': yMax,
        'char': char,
        'R1': xMin,
        'R2': xMax,
        'Newform': None,
        'weight': weight
    }
    projection = ['maass_id', 'Eigenvalue', 'Level', 'Symmetry']
    forms = maass_db.get_Maass_forms(search, projection, sort=[], limit=10000)

    # Loop through all forms and add a clickable dot for each
    for f in forms:
        linkurl = L + "/ModularForm/GL2/Q/Maass/{0}".format(f['maass_id'])
        x = (f['Eigenvalue'] - xMin) * xfactor + xshift
        y = (f['Level'] - yMin + 1) * yfactor
        try:  # Shifting even slightly up and odd slightly down
            if f['Symmetry'] == 0 or f['Symmetry'] == 'even':
                y -= 1
                color = signtocolour(1)
            elif f['Symmetry'] == 1 or f['Symmetry'] == 'odd':
                y += 1
                color = signtocolour(-1)
            else:
                color = signtocolour(0)
        except Exception:
            color = signtocolour(0)

        ans += "<a xlink:href='{0}' target='_top'>".format(linkurl)
        ans += "<circle cx='{0}' cy='{1}' ".format(str(x)[0:6], str(y))
        ans += "r='{0}'  style='fill:{1}'>".format(str(radius), color)
        ans += "<title>{0}</title></circle></a>\n".format(f['Eigenvalue'])

    ans += "</svg>"
    return ans
Пример #9
0
def paintSvgHoloGeneral(Nmin, Nmax, kmin, kmax, imagewidth, imageheight):
    # the import must be here to avoid circular import
    from lmfdb.classical_modular_forms.web_newform import WebNewform
    from lmfdb.classical_modular_forms.web_space import WebGamma1Space
    xfactor = 90
    yfactor = 30
    extraSpace = 20
    ticlength = 4
    radius = 3.3
    xdotspacing = 0.30  # horizontal spacing of dots
    ydotspacing = 0.11  # vertical spacing of dots
    # colourplus = signtocolour(1) # not used
    # colourminus = signtocolour(-1) # not used
    maxdots = 5  # max number of dots to display

    ans = svgBegin()

    xMax = int(Nmax)
    yMax = int(kmax)
    width = xfactor * xMax + extraSpace
    height = yfactor * yMax + extraSpace

    # make the coordinate system
    ans += paintCSHoloTMP(width, height, xMax, yMax, xfactor, yfactor,
                          ticlength)
    alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

    # create appearanceinfo, common to all points
    appearanceinfo = []

    # loop over levels and weights, using plotsector to put the appropriate dots at each lattice point
    for x in range(int(Nmin), int(Nmax) + 1):  # x is the level
        for y in range(int(kmin), int(kmax) + 1, 2):  # y is the weight
            # lid = "(" + str(x) + "," + str(y) + ")" # not used
            # linkurl = "/L/ModularForm/GL2/Q/holomorphic/" + str(y) + "/" + str(x) + "/1/" # not used
            WS = WebGamma1Space(
                level=x,
                weight=y)  # space of modular forms of weight y, level x
            galois_orbits = WS.decomp  # make a list of Galois orbits
            numlabels = len(galois_orbits)  # one label per Galois orbit
            thelabels = alphabet[
                0:
                numlabels]  # list of labels for the Galois orbits for weight y, level x
            # countplus = 0   # count how many Galois orbits have sign Plus (+ 1) (not used)
            # countminus = 0   # count how many Galois orbits have sign Minus (- 1) (not used)
            # ybaseplus = y  # baseline y-coord for plus cases (not used)
            # ybaseminus = y  # baseline y-coord for minus cases (not used)
            # numpluslabels = 0 # not used
            # numminuslabels = 0 # not used
            # plotsector requires three dictionaries: dimensioninfo, appearanceinfo, and urlinfo
            # create dimensioninfo
            dimensioninfo = {}
            dimensioninfo['offset'] = [0, height]
            dimensioninfo['scale'] = [xfactor, -1 * yfactor]
            dimensioninfo['vertexlocation'] = [x, y]
            dimensioninfo['maxdots'] = maxdots
            dimensioninfo['dotspacing'] = [xdotspacing, ydotspacing]
            dimensioninfo['edge'] = [[0, 1], [1, 0]
                                     ]  # unit vectors defining edges of sector
            # dimensioninfo['edgelength'] = [float(dimensioninfo['scale'][0])/float(Nmax), float(dimensioninfo['scale'][1])/float(kmax)] #add comment
            dimensioninfo['edgelength'] = [0.5, 0.5]
            dimensioninfo['dotradius'] = radius
            dimensioninfo[
                'connectinglinewidth'] = dimensioninfo['dotradius'] / 1.5
            dimensioninfo['firstdotoffset'] = [0.0, 0.0]
            #
            appearanceinfo = {}
            # appearanceinfo['edgewidth'] = dimensioninfo['dotspacing'][0]/1.0  #just a guess
            appearanceinfo['edgewidth'] = [0, 0]  # remove the sector edges
            appearanceinfo['edgestyle'] = 'stroke-dasharray:3,3'
            appearanceinfo['edgecolor'] = 'rgb(202,202,102)'
            appearanceinfo['fontsize'] = 'font-size:11px'
            appearanceinfo['fontweight'] = ""
            #
            urlinfo = {'base': '/L/ModularForm/GL2/Q/holomorphic?'}
            urlinfo['space'] = {'weight': y}
            urlinfo['space']['level'] = x
            urlinfo['space']['character'] = 0
            #
            # scale = 1 # not used
            # Symmetry types: +1 or -1
            symmetrytype = [1, -1]
            for signtmp in symmetrytype:
                # urlinfo['space']['orbits'] = [ [] for label in thelabels ] # initialise
                # an empty list for each orbit
                urlinfo['space']['orbits'] = []
                for label in thelabels:  # looping over Galois orbit: one label per orbit
                    # do '+' case first
                    MF = WebNewform.by_label(
                        label=label
                    )  # one of the Galois orbits for weight y, level x
                    numberwithlabel = MF.degree(
                    )  # number of forms in the Galois orbit
                    if x == 1:  # For level 1, the sign is always plus
                        signfe = 1
                    else:
                        # signfe = -1
                        frickeeigenvalue = prod(MF.atkin_lehner_eigenvalues(
                        ).values())  # gives Fricke eigenvalue
                        signfe = frickeeigenvalue * (-1)**float(
                            y / 2)  # sign of functional equation
                    if signfe == signtmp:  # we find an orbit with sign of "signtmp"
                        if signfe == 1:
                            dimensioninfo['edge'] = [[0, 1], [1, 0]]
                            # unit vectors defining edges of sector for signfe positive
                        else:
                            # dimensioninfo['edge'] = [[0,1],[-1,0]]     # unit vectors defining edges
                            # of sector for signfe negative
                            dimensioninfo['edge'] = [[0, -1], [-1, 0]]
                            # unit vectors defining edges of sector for signfe negative
                        dimensioninfo['dotspacing'] = [
                            signfe * xdotspacing, ydotspacing
                        ]
                        dimensioninfo['firstdotoffset'] = [
                            0.5 * (dimensioninfo['dotspacing'][0] *
                                   dimensioninfo['edge'][0][0] +
                                   dimensioninfo['dotspacing'][1] *
                                   dimensioninfo['edge'][1][0]), 0
                        ]
                        signcolour = signtocolour(signfe)
                        appearanceinfo['edgecolor'] = signcolour
                        orbitdescriptionlist = []
                        for n in range(numberwithlabel):
                            orbitdescriptionlist.append({
                                'label': label,
                                'number': n,
                                'color': signcolour
                            })
                        urlinfo['space']['orbits'].append(orbitdescriptionlist)
                # urlinfo['space']['orbits'][0][0]['color'] = signtocolour(-1)
                # appearanceinfo['orbitcolor'] = 'rgb(102,102,102)'
                    ans += plotsector(dimensioninfo, appearanceinfo, urlinfo)

    ans += svgEnd()
    return (ans)
Пример #10
0
def paintSvgHolo(Nmin, Nmax, kmin, kmax):
    # the import must be here to avoid circular import
    from lmfdb.classical_modular_forms.web_space import WebGamma1Space
    xfactor = 90
    yfactor = 30
    extraSpace = 20
    ticlength = 4
    radius = 3.3
    xdotspacing = 0.11  # horizontal spacing of dots
    ydotspacing = 0.28  # vertical spacing of dots
    colourplus = signtocolour(1)
    colourminus = signtocolour(-1)
    maxdots = 5  # max number of dots to display

    ans = svgBegin()

    xMax = int(Nmax)
    yMax = int(kmax)
    width = xfactor * xMax + extraSpace
    height = yfactor * yMax + extraSpace

    ans += paintCSHolo(width, height, xMax, yMax, xfactor, yfactor, ticlength)

    # alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

    # loop over levels and weights
    for x in range(int(Nmin), int(Nmax) + 1):  # x is the level
        for y in range(int(kmin), int(kmax) + 1):  # y is the weight
            # lid = "(" + str(x) + "," + str(y) + ")" # not used
            linkurl = "/L/ModularForm/GL2/Q/holomorphic/" + str(x) + "/" + str(
                y) + "/1/"
            try:
                WS = WebGamma1Space(level=x, weight=y)
            except ValueError:
                continue
            newspaces = WS.decomp
            # numlabels = len(WS.decomp)  # one label per Galois orbit
            # thelabels = alphabet[0:numlabels]    # list of labels for the Galois orbits for weight y, level x
            # countplus = 0   # count how many Galois orbits have sign Plus (+ 1) # not used
            # countminus = 0   # count how many Galois orbits have sign Minus (- 1) # not used
            ybaseplus = y  # baseline y-coord for plus cases
            ybaseminus = y  # baseline y-coord for minus cases
            numpluslabels = 0
            numminuslabels = 0
            for newsp in newspaces:  # looping over Galois orbit
                for MF in newsp[1]:
                    print(MF)
                    linkurl = "/L/ModularForm/GL2/Q/holomorphic/%d/%d/%s/%s/" % (
                        x, y, MF['char_orbit_label'],
                        cremona_letter_code(MF['hecke_orbit'] - 1))
                    numberwithlabel = MF[
                        'dim']  # number of forms in the Galois orbit
                    # frickeeigenvalue = prod(MF.atkin_lehner_eigenvalues().values())  # gives Fricke eigenvalue
                    self_dual = MF['char_is_real'] * (-1)**float(
                        y / 2)  # sign of functional equation
                    xbase = x - self_dual * (xdotspacing / 2.0)

                    if self_dual > 0:  # go to right in BLUE if plus
                        ybase = ybaseplus
                        ybaseplus += ydotspacing
                        thiscolour = colourplus
                        numpluslabels += 1
                    else:  # go to the left in RED of minus
                        ybase = ybaseminus
                        ybaseminus += ydotspacing
                        thiscolour = colourminus
                        numminuslabels += 1

                    if numberwithlabel > maxdots:  # if more than maxdots in orbit, use number as symbol
                        xbase += 1.5 * self_dual * xdotspacing
                        if self_dual < 0:  # move over more to position numbers on minus side.
                            xbase += self_dual * xdotspacing
                        ybase += -0.5 * ydotspacing
                        if (self_dual > 0 and numpluslabels > 1) or (
                                self_dual < 0 and numminuslabels > 1):
                            ybase += ydotspacing
                        ans += "<a xlink:href='" + url_for(
                            'not_yet_implemented') + "' target='_top'>\n"

                        #  TODO: Implement when there is more than maxdots forms

                        ans += ("<text x='" +
                                str(float(xbase) * xfactor)[0:7] + "' y='" +
                                str(height - float(ybase) * yfactor)[0:7] +
                                "' style='fill:" + thiscolour +
                                ";font-size:14px;font-weight:bold;'>" +
                                str(numberwithlabel) + "</text>\n")
                        ans += "</a>\n"
                        if self_dual < 0:
                            ybaseminus += 1.5 * ydotspacing
                        else:
                            ybaseplus += 1.5 * ydotspacing
                    else:  # otherwise, use one dot per form in orbit, connected with a line
                        if numberwithlabel > 1:  # join dots if there are at least two
                            # add lines first and then dots to prevent line from hiding link
                            firstcenterx = xbase + self_dual * xdotspacing
                            firstcentery = ybase
                            lastcenterx = xbase + (numberwithlabel *
                                                   self_dual * xdotspacing)
                            lastcentery = ybase
                            ans += "<line x1='%s' " % str(
                                float(firstcenterx) * xfactor)[0:7]
                            ans += "y1='%s' " % str(
                                float(height - firstcentery * yfactor))[0:7]
                            ans += "x2='%s' " % str(
                                float(lastcenterx) * xfactor)[0:7]
                            ans += "y2='%s' " % str(
                                float(height - lastcentery * yfactor))[0:7]
                            ans += "style='stroke:%s;stroke-width:2.4'/>" % thiscolour
                        for number in range(0, numberwithlabel):
                            xbase += self_dual * xdotspacing
                            ans += "<a xlink:href='" + linkurl + str(
                                number + 1) + "/' target='_top'>\n"
                            ans += "<circle cx='" + str(
                                float(xbase) * xfactor)[0:7]
                            ans += "' cy='" + str(height -
                                                  float(ybase) * yfactor)[0:7]
                            ans += "' r='" + str(radius)
                            ans += "' style='fill:" + thiscolour + "'>"
                            ans += "<title>" + str((x, y)).replace(
                                "u", "").replace("'", "") + "</title>"
                            ans += "</circle></a>\n"

    ans += svgEnd()
    return ans
Пример #11
0
def paintSvgFileAll(glslist):  # list of group and level
    xfactor = 20
    yfactor = 20
    extraSpace = 20
    ticlength = 4
    radius = 3

    ans = svgBegin()

    paralist = []
    xMax = 0
    yMax = 0
    for gls in glslist:
        group = gls[0]
        level = gls[1]

        for l in db.lfunc_lfunctions.search(
            {
                'group': group,
                'conductor': level
            }, ['origin', 'root_number']):
            splitOrigin = l['origin'].split('/')
            char = splitOrigin[5]
            R = splitOrigin[6]
            ap_id = splitOrigin[7]
            splitId = R.split('_')
            paralist.append((splitId[0], splitId[1], l['origin'], group, level,
                             char, R, ap_id, l['root_number']))
            if float(splitId[0]) > xMax:
                xMax = float(splitId[0])
            if float(splitId[1]) > yMax:
                yMax = float(splitId[1])

    xMax = int(math.ceil(xMax))
    yMax = int(math.ceil(yMax))
    width = xfactor * xMax + extraSpace
    height = yfactor * yMax + extraSpace

    ans += paintCS(width, height, xMax, yMax, xfactor, yfactor, ticlength)
    for (x, y, lid, group, level, char, R, ap_id, sign) in paralist:
        if float(x) > 0 and float(y) > 0:  # Only one of dual pair
            try:
                linkurl = url_for('.l_function_maass_gln_page',
                                  group=group,
                                  level=level,
                                  char=char,
                                  R=R,
                                  ap_id=ap_id)
            except Exception:  # catch when running a test
                linkurl = lid
            ans += "<a xlink:href='" + linkurl + "' target='_top'>\n"
            ans += "<circle cx='" + str(float(x) * xfactor)[0:7]
            ans += "' cy='" + str(height - float(y) * yfactor)[0:7]
            ans += "' r='" + str(radius)
            ans += "' style='fill:" + signtocolour(sign) + "'>"
            ans += "<title>" + str(
                (x, y)).replace("u", "").replace("'", "") + "</title>"
            ans += "</circle></a>\n"

    ans += svgEnd()
    return (ans)
Пример #12
0
def paintSvgMaass(min_level,
                  max_level,
                  min_R,
                  max_R,
                  width=1000,
                  heightfactor=20,
                  L=""):
    ''' Returns the contents (as a string) of the svg-file for
        all Maass forms in the database of the specified weight.
        Takes all levels from min_level to max_level
        Spectral parameter in [min_R, max_R]
        Set L="/L" to make link go to the L-function
    '''
    xMax = int(max_R)
    yMax = int(max_level)
    xMin = int(min_R)
    yMin = int(min_level)
    extraSpace = 40
    length_R = xMax - xMin
    length_level = yMax - yMin + 1
    if length_level < 30:
        heightfactor = heightfactor * 2
    height = length_level * heightfactor + extraSpace
    xfactor = (width - extraSpace) / length_R
    yfactor = (height - extraSpace) / length_level
    ticlength = 4
    radius = 3
    xshift = extraSpace

    # Start of file and add coordinate system
    ans = "<svg  xmlns='http://www.w3.org/2000/svg'"
    ans += " xmlns:xlink='http://www.w3.org/1999/xlink'"
    ans += " height='{0}' width='{1}'>\n".format(height + 20, width + 20)
    ans += paintCSMaass(width, height, xMin, xMax, yMin, yMax, xfactor,
                        yfactor, ticlength, xshift)

    # Fetch Maass forms from database
    forms = db.maass_newforms.search(
        {
            'spectral_parameter': {
                '$gte': xMin,
                '$lte': xMax
            },
            'level': {
                '$gte': yMin,
                '$lte': yMax
            }
        }, ["maass_id", "spectral_parameter", "level", "symmetry"],
        sort=[("level", 1), ("symmetry", -1), ("spectral_parameter", 1),
              ("maass_id", 1)])

    # Loop through all forms and add a clickable dot for each
    for f in forms:
        linkurl = L + url_for("maass.by_label", label=f['maass_id'])
        x = (f['spectral_parameter'] - xMin) * xfactor + xshift
        y = (f['level'] - yMin + 1) * yfactor
        s = f.get('symmetry', 0)
        y -= s  # Shifting even slightly up and odd slightly down
        color = signtocolour(s)

        ans += "<a xlink:href='{0}' target='_top'>".format(linkurl)
        ans += "<circle cx='{0}' cy='{1}' ".format(str(x)[0:6], str(y))
        ans += "r='{0}'  style='fill:{1}'>".format(str(radius), color)
        ans += "<title>{0}</title></circle></a>\n".format(
            f['spectral_parameter'])

    ans += "</svg>"
    return ans