Student volunteers needed for web college redesign

The college is redesigning its website, and we need student feedback on what you think of the site — what works, what doesn’t, and what should change. We’ll be conducting focus groups, usability tests, and surveys over the next few months to gather this information.

If you’d like to help out, please fill out this form:

https://lafayettec.az1.qualtrics.com/SE/?SID=SV_b8W23pBeHDAWW4l

Once you do so we’ll contact you about opportunities to help.

Posted in Web Design | Tagged | Leave a comment

Volunteers needed for Res Life web usability test

We need help from students to improve the Residence Life website. We’re going to be running usability tests of the Res Life web presence on Thursday 3/27 from 9:30 a.m. until noon. The test takes about 30 minutes and consists of volunteers attempting to complete a series of housing-related tasks.

If you’re interested in helping out, sign up to be a usability testing volunteer using this Qualtrics form:

https://lafayettec.qualtrics.com/SE/?SID=SV_cOr5VKrsjaykEqV

Once you’ve signed up we’ll send you more information about how to volunteer for Thursday’s test.

Contact Ken Newquist (newquisk@lafayette.edu) for more information.

Posted in Web Design | Tagged | Leave a comment

Behind the new Lafayette Web Login Page

The Lafayette College Web Login page — which people use to log into single sign-on enabled web apps like Webmail, My Lafayette, Moodle, and The List – has been redesigned to work better on mobile devices and to tell you more about what you’re logging into.

The big change is the look and feel of the login page. Prior to the redesign the page worked just fine when you were logging into a web application using your desktop computer. It got increasingly difficult to use with smaller devices, until you had to resort to the dreaded pinch-zoom technique to focus in on the username and password fields.

We fixed this by making the login page responsive, which means the page re-flows itself to fit the device you’re using. We did this using Bootstrap, a responsive frontend web framework that is designed to work well with mobile devices.

All this means that logging into Webmail just got a heck of a lot easier from your smartphone.

Another big change is the addition of service descriptions. It used to be that if you logged into something like the My Lafayette you’d get a generic message that you were able to single sign-on into something … but it wouldn’t tell you what that thing was.

We’ve made the login page smarter so it now knows where you’re going. If you’re logging into My Lafayette, it tells you that by showing an app-specific icon and giving you a brief description of the service. This in turn meant that we could start directly linking to things like the college portal from the home page because the links were no longer a dead end for unauthenticated visitors.

For example, if the “Current Students” link in the header of the Lafayette home page had gone to the portal before, parents clicking on the link wouldn’t know where they were going (and why they couldn’t get in). As a result when the portal launched in Fall 2013 we didn’t change that link.

The new login page takes care of that potential confusion, so we’ve got over “Current Students” to point to the new portal rather than the old student landing page. From a technical perspective, this was accomplished by implementing Unicon’s CAS-addons code for the CAS JSON service registry.

The service description is great, but we don’t want it to clutter up your screen when you’re trying to login form your phone. Thanks to responsive design, when folks are visiting using their phones (or a smaller tablet like the iPad Mini) we can slide that text down and out of the way so that the login form is front and center.

Here’s what it looks like for desktop web browsers:

An example of the desktop version of the login page.

An example of the desktop version of the login page.

And here’s the same page for smartphones:

Smartphone view of the Login page.

An example of the smartphone view of the login page.

We’ve also implemented this same design on google.lafayette.edu, though the effect isn’t as pronounced there because it’s a single column webpage. Regardless, it’s still easier to use on your phone because you no longer need to pinch-and-zoom to use the site:

The responsive site search form at google.lafayette.edu.

The responsive site search form at google.lafayette.edu.

What do you think of the new login form? Post your thoughts as a comment or email us at itsblog@lafayette.edu.

 

 

Posted in Technology, Updates, Web Design | Tagged | Leave a comment

New features added to the college calendar

The College Calendar has been enhanced with a number of community-requested features. These updates include:

Improved Navigation: Visitors can move backward and forward between days, weeks, and months using the “previous” and “next” buttons in the blue header at the top of the page. The calendar also remembers what day/week/month visitors were viewing when moving between categories.

Recently Added Events: We’ve created a new page that lists all of the events added to the calendar in the last seven days, newest events first. Visitors can access the list through the “Tasks” menu in the sidebar or by going to the webpage directly: https://calendar.lafayette.edu/recently-added-events

Search: The calendar’s search capabilities have been upgraded, and we’ve added a new “Search” field to the top of the right-hand column.

Cloning Events: We’ve made it easier to create multiple events by adding a clone feature. Now when authenticated users visit events they created, they will see a “clone content” tab. Clicking the tab makes a copy that can be edited and saved as a new event.

Ongoing Events: The ongoing events block now shows up in the day/week/month views in addition to the category views.

Posted in Updates, Web Design | Tagged | Leave a comment

Web usability testing volunteers needed

We want to improve the Lafayette web presence, and we need your help to do it.

Lafayette’s web team is conducting usability testing of the college’s websites. During these short 30-minute tests, volunteers attempt to complete a series of tasks, such as using the college’s search engine to find something or adding an event to the online calendar. We evaluate the results, determine what needs improvement, and then fix it.  If you’d like to help, please fill out our volunteer form.

You’ll be added to our usability testing pool and then contacted whenever a new test is scheduled. We typically run 3-4 tests a semester, and you can choose which ones you’d like to participate in.

Posted in Technology | Tagged | Leave a comment

Math as Art

Sometimes, math can be really beautiful.  Image processing has always been a favorite subject of mine, and I love to see some of the really cool effects you can achieve by applying mathematical transformations to images.  While browsing a gallery of my favorite data plotting tool, matplotlib, I stumbled across what I considered a very pretty effect (item “showcase 9″).

It is based on a Voronoi diagram, which is an interesting topic for the mathematically inclined in its own right.  I was able to piece together some code from a number of web resources to generalize the effect and apply it to various images.

Below is an example and the code.  It is quite mosiac-like.  Enjoy!

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

def circumcircle(P1,P2,P3):
    ''' 
    Adapted from:
    http://local.wasp.uwa.edu.au/~pbourke/geometry/circlefrom3/Circle.cpp
    '''
    delta_a = P2 - P1
    delta_b = P3 - P2
    epsilon = 0.000000001
    if np.abs(delta_a[0]) <= epsilon and np.abs(delta_b[1]) <= epsilon:
        center_x = 0.5*(P2[0] + P3[0])
        center_y = 0.5*(P1[1] + P2[1])
    else:
        aSlope = delta_a[1]/delta_a[0]
        bSlope = delta_b[1]/delta_b[0]
        if np.abs(aSlope-bSlope) <= epsilon:
            return None
        center_x= (aSlope*bSlope*(P1[1] - P3[1]) + bSlope*(P1[0] + P2 [0]) \
                        - aSlope*(P2[0]+P3[0]) )/(2* (bSlope-aSlope) )
        center_y = -1*(center_x - (P1[0]+P2[0])/2)/aSlope +  (P1[1]+P2[1])/2;
    radius = np.sqrt( (center_x - P1[0])**2+(center_y - P1[1])**2)
    return center_x, center_y, radius

def voronoi(X,Y):
    P = np.zeros((X.size,2))
    P[:,0] = X
    P[:,1] = Y
    D = matplotlib.tri.Triangulation(X,Y)
    T = D.triangles
    n = T.shape[0]
    C = np.zeros((n,3))

    # Get circle for each triangle, center will be a voronoi cell point
    cells = []
    for i in range(X.size):
        cells.append( list() )
    for i in range(n):
        C[i] = circumcircle(P[T[i,0]],P[T[i,1]],P[T[i,2]])
        x,y,r = C[i]
        cells[T[i,0]].append( (x,y) )
        cells[T[i,1]].append( (x,y) )
        cells[T[i,2]].append( (x,y) )

    # Reordering cell points in trigonometric way
    for i,cell in enumerate(cells):
        xy = np.array(cell)
        I = np.argsort(np.arctan2(xy[:,1]-Y[i],xy[:,0]-X[i]))
        cell = xy[I].tolist()
        cell.append(cell[0])
        cells[i] = cell
    return cells

def tileize(img, ax=None, tile_factor=50):
    X,Y = np.meshgrid(np.linspace(-0.1,1.1,tile_factor), np.linspace(-0.1,1.1,tile_factor))
    X = X.ravel() + np.random.uniform(-0.0125,0.0125,X.size)
    Y = Y.ravel() + np.random.uniform(-0.0125,0.0125,Y.size)
    cells = voronoi(X,Y)

    if ax is None:
        fig = plt.figure(figsize=(8,6))
        axes = plt.subplot(111, aspect=1)
    else:
        axes = ax
    for i,cell in enumerate(cells):
        codes = [matplotlib.path.Path.MOVETO] \
            + [matplotlib.path.Path.LINETO] * (len(cell)-2) \
            + [matplotlib.path.Path.CLOSEPOLY]
        path = matplotlib.path.Path(cell,codes)

        x,y = 1-max(min(Y[i],1),0), max(min(X[i],1),0)
        x = int(x*(img.shape[0]-1))
        y = int(y*(img.shape[1]-1))
        color = img[x,y]
        patch = matplotlib.patches.PathPatch(
            path, facecolor=color, edgecolor='w', linewidth=.5, zorder=-1)
        axes.add_patch(patch)

    plt.axis([0,1,0,1])
    plt.xticks([]), plt.yticks([])
    
import matplotlib.image as mpimg
fig = figure(figsize=(17,7))
ax = fig.add_subplot(121)
img = mpimg.imread('./images/quad7.png') #Example image.
ax.imshow(img)
ax = fig.add_subplot(122)
tileize(img, ax=ax, tile_factor=100)

quad_mosaic
Posted in Uncategorized | Tagged , , | Leave a comment

Another Plot Surfaces

Some time ago I wrote about plotting 3D projections using Python and matplotlib .  The figure I plotted was a MATLAB example from Wikipedia.  Since that time, I have been able to actually play with MATLAB for a bit.  I translated some MATLAB plots to matplotlib.  The results and code are below.  The matplotlib code is admittedly a bit more verbose, but in some cases the explicitness really helped me understand what was going on.  MATLAB tends to have a lot of short cuts built in for common scenarios that will make sense to the adept, but not the beginner, in my opinion.

I “borrowed” the cylinder code from this nice blog entry.  I hope my comments help out anyone who was thinking of translating their MATLAB code into Python.  This can be really useful if you want to integrate your other computing systems with some cool 3D plots.  The MATLAB GUI is certainly easy and fun to play around with, but code integration with other systems seems to be a lot easier with matplotlib.

I created all these examples in an IPython notebook.  If you want to run them from a stand alone script, you will need to add code to show or save the plot (e.g. plt.show()).

MATLAB Code
[X, Y] = meshgrid(-2:.2:2);
Z = X .* exp(-X.^2 -Y.^2);
surf(X,Y,Z);

#Matplotlib code
from matplotlib import cm  #Color maps
from matplotlib import pyplot as plt  #The MATLAB-like interface to matplotlib
from mpl_toolkits.mplot3d import Axes3D #Need this for a '3d' projection
import numpy as np #NumPy is used for working with vectors

v = np.arange(-2, 2, 0.2)
X, Y = np.meshgrid(v, v) #In MATLAB, meshgrid(vector) is shorthand for meshgrid(vector, vector)
Z = X * np.exp(-X**2 -Y**2)

#Create a figure
fig = plt.figure(figsize=(10,10))
#Create 3D axes in the figure.
ax = fig.gca(projection='3d')
#Plot the surface.
surf = ax.plot_surface(X,Y,Z, cmap=cm.jet, rstride=1, cstride=1)
#Adjust the viewing angle.
ax.view_init(elev=20.0, azim=250.0)

mpl_surf_01
MATLAB Code
t = 0:pi/10:2*pi; 
[X,Y,Z] = cyclinder(4*cos(t)); 
surf(X, Y, Z);


#Matplotlib code
# Cylinder function
def cylinder(r,n):
    '''
    Returns the unit cylinder that corresponds to the curve r.
    INPUTS:  r - a vector of radii
             n - number of coordinates to return for each element in r

    OUTPUTS: x,y,z - coordinates of points
    '''
    # ensure that r is a column vector
    r = np.atleast_2d(r)
    r_rows,r_cols = r.shape
    
    if r_cols > r_rows:
        r = r.T

    # find points along x and y axes
    points  = np.linspace(0,2*np.pi,n+1)
    x = np.cos(points)*r
    y = np.sin(points)*r

    # find points along z axis
    rpoints = np.atleast_2d(np.linspace(0,1,len(r)))
    z = np.ones((1,n+1))*rpoints.T
    
    return x,y,z

#Plot translation MATLAB -> Matplotlib
from matplotlib import cm  #Color maps
from matplotlib import pyplot as plt  #The MATLAB-like interface to matplotlib
from mpl_toolkits.mplot3d import Axes3D #Need this for a '3d' projection
import numpy as np #NumPy is used for working with vectors

t = np.arange(0.0, 2.0*np.pi, np.pi/10.0)
X, Y, Z = cylinder(4.0 * np.cos(t), 20)

#Create a figure
fig = plt.figure(figsize=(10,10))
#Create 3D axes in the figure.
ax = fig.gca(projection='3d')

#Plot the surface.
surf = ax.plot_surface(X,Y,Z, cmap=cm.jet, rstride=1, cstride=1)
#Adjust the viewing angle.
ax.view_init(elev=20.0, azim=250.0)

mpl_cylinder_surf_01
MATLAB Code
[X,Y] = meshgrid(-8:.5:8); 
R = sqrt(X.^2 + Y.^2) + eps; 
Z = sin(R)./R mesh(X,Y,Z);

#Matplotlib code
from matplotlib.cm import get_cmap  #Color maps
# Generate colors.
def colorgen(num_colors=22, colormap='Paired'):
    """
    """
    cm = get_cmap(colormap)
    colors = [cm(1.*i/num_colors) for i in range(num_colors)]
    return colors

# Main plot code
from matplotlib import cm  #Color maps
from matplotlib import pyplot as plt  #The MATLAB-like interface to matplotlib
from mpl_toolkits.mplot3d import Axes3D #Need this for a '3d' projection
import numpy as np #NumPy is used for working with vectors

v = np.arange(-8.0, 8.0, 0.5)
X, Y = np.meshgrid(v, v) #In MATLAB, meshgrid(vector) is shorthand for meshgrid(vector, vector)
R = np.sqrt(X**2 + Y**2) + np.finfo(float).eps
Z = np.sin(R)/R

#Create a figure
fig = plt.figure(figsize=(10,10))
#Create 3D axes in the figure.
ax = fig.gca(projection='3d')
#Plot.
ax.plot_wireframe(X,Y,Z, colors=colorgen(colormap='jet'))

mpl_mesh_01

MATLAB Code
t = 0:pi/10:2*pi; 
[X,Y,Z] = cyclinder(4*cos(t)); 
subplot(2,2,1); 
mesh(X); title('X'); 
subplot(2,2,2); mesh(Y); 
title('Y'); 
subplot(2,2,3); 
mesh(Z); 
title('Z'); 
subplot(2,2,4); 
mesh(X,Y,Z); 
title('X,Y,Z');

from matplotlib import cm  #Color maps
from matplotlib import pyplot as plt  #The MATLAB-like interface to matplotlib
from mpl_toolkits.mplot3d import Axes3D #Need this for a '3d' projection
import numpy as np #NumPy is used for working with vectors

def plot_meshgrid(Z, ax=None, **kwds):
    """
    """
    if ax is None:
        ax = gca()
    n, m = Z.shape
    vecx = np.linspace(1,m,num=m)
    vecy = np.linspace(1,n,num=n)
    x,y = np.meshgrid(vecx,vecy)
    ax.plot_wireframe(x,y,Z, **kwds)

#v = np.linspace(-8.0, 8.0, num=33)
t = np.arange(0.0, 2.0*np.pi, np.pi/10.0)
X, Y, Z = cylinder(4.0 * np.cos(t), 20)

#Create a figure
fig = plt.figure(figsize=(10,10))


#Create 3D axes in the figure.
#ax = fig.gca(projection='3d')
#Plot.
ax = fig.add_subplot(221, projection='3d')
plot_meshgrid(X, ax=ax, colors=colorgen(colormap='jet'))
ax.set_title('X')

ax = fig.add_subplot(222, projection='3d')
plot_meshgrid(Y, ax=ax, colors=colorgen(colormap='jet'))
ax.set_title('Y')

ax = fig.add_subplot(223, projection='3d')
plot_meshgrid(Z, ax=ax, colors=colorgen(colormap='jet'))
ax.set_title('Z')

ax = fig.add_subplot(224, projection='3d')
ax.plot_wireframe(X,Y,Z, colors=colorgen(colormap='jet'))
ax.set_title('X,Y,Z')

mpl_mesh_02
 
 

Posted in Technology | Tagged , | Leave a comment

Plotting Your Own Course With Basemap

I had previously written about the Python library maplotlib and the cool 2D and 3D plots you could create with it.  Recently, I noticed that a lot of folks have been building tool kits on top of matplotlib for various scientific domains.  One toolkit that caught my eye was Basemap, a library used to plot data on maps.

What is really cool about Basemap is that it offers 30 different map projections of the Earth, right out of the box.  And since the underlying data plotting mechanisms are all based on matplotlib, it didn’t take me long to figure out how to put markers on my location.

I stumbled upon a really nice tutorial for Basemap.  The earlier lessons were a great supplement to the Basemap documentation.  The tutorial I linked to shows how the author plotted earthquake data on a map using basemap.  This article was 8 months old, though, and the government web service seemed to have changed.  Also, I had recently been playing with matplotlib’s image processing capabilities, and I wanted to put a color bar on my plots as a key to show what the colors meant data-wise.  So I shamelessly “borrowed” some of the author’s code and updated the data extraction and parsing, and added some new features to the plot.  The result is the map I generated below:

Earthquake data plotted with matplotlib and Basemap

Earthquake data plotted with matplotlib and Basemap

I created this interactively using an IPython notebook.  If you are not familiar with IPython, it is a really cool interactive shell for Python.  And the notebook feature lets you run the shell as a back end process that runs a mini web server so you can have an interactive Python session in your web browser.  Best of all, plots from matplotlib can be rendered right in the browser.  These notebooks can be saved and shared.

The code from my notebook is below.  If you want to run it from a script, you will need to add a statement to show or save the plot at the end of the script (e.g. plt.show()).  I have a variable, LIVE_DATA, defined and set to True.  This causes the the data to be loaded from the USGS government web service.  If you want to experiment and don’t want to keep downloading the data, you can download the data once by using curl or even just placing the HTTP GET request in your browser and saving the result to a file.  Set LIVE_DATA to False and make sure the code is attempting to load from the correct file (I was loading from ‘earthquake.json’ in the folder I launched the notebook from).  You may also want to experiment with various map types.  I chose ‘moll’ for this demonstration, but I also played with ‘robin’ and ‘ortho’.  Some types of maps require different information, so you may need to add or change parameters.

#=======================
# Plot Earthquake data.
#=======================

#Standard library
import datetime
from dateutil.tz import tzutc, tzlocal

#External modules.
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from matplotlib.cm import get_cmap, ScalarMappable
from mpl_toolkits.basemap import Basemap
import numpy as np

#Time zone info.
tz_utc = tzutc()
tz_local = tzlocal()

#Set figure size.
fig = figure(figsize=(14,10))

#Choose map type.
# make sure the value of resolution is a lowercase L,
#  for 'low', not a numeral 1
#m = Basemap(projection='ortho', lat_0=50, lon_0=-100, resolution='l', area_thresh=1000.0)
m = Basemap(projection='moll', lat_0=0, lon_0=-100, resolution='l', area_thresh=1000.0)
#Fill in map features.
r = m.drawcoastlines()
r = m.fillcontinents(color='coral',lake_color='aqua')
r = m.drawmapboundary(fill_color='aqua')
r = m.drawmeridians(np.arange(0, 360, 30))
r = m.drawparallels(np.arange(-90, 90, 30))

#Load earthquake data (either live or from file).
LIVE_DATA = True
import json
if LIVE_DATA:
    import urllib
    end_dt = datetime.datetime.today().replace(tzinfo=tz_local)
    start_dt = (end_dt - datetime.timedelta(days=1))
    starttime = start_dt.strftime("%Y%m%dT%H:%M:%S%z")
    endtime = end_dt.strftime("%Y%m%dT%H:%M:%S%z")
    f = urllib.urlopen("http://comcat.cr.usgs.gov/fdsnws/event/1/query?starttime=%s&endtime=%s&format=geojson&eventtype=earthquake" % (
                            starttime, endtime))
    o = json.load(f)
    f.close()
    title = "Earthquake Magnitudes %s - %s" % (start_dt.strftime("%d %b %Y %H:%M %Z"), end_dt.strftime("%d %b %Y %H:%M %Z"))
else:
    title = "Earthquake Magnitudes - Sep. 25 2013 - Sep. 26 2013"
    f = open("./earthquake.json", "r")
    o = json.load(f)
    f.close()

#Parse data and extract the values in which we are interested.
features = o['features']
data = []
for feature in features:
    geo = feature['geometry']
    coords = geo['coordinates']
    lon, lat = coords[0], coords[1]
    props = feature['properties']
    mag = props['mag']
    data.append((lon, lat, mag))
del o

#Set up a normalizer and color map for the scatter plot.
norm = Normalize()
cmap = get_cmap('jet')
#Create series for longitude, latitude, and magnitude.
lons = []
lats = []
mags = []
for lon, lat, mag in data:
    xpt,ypt = m(lon,lat)
    lons.append(xpt)
    lats.append(ypt)
    mags.append(mag)
x = np.array(lons)
y = np.array(lats)
#Create normalized magnitudes (range [0, 1.0]) for color map.
mag_norms = norm(np.array(mags))
#Create marker sizes as a function of magnitude.
z = (mag_norms * 10.0)**2.0
#Plot the data and create a colorbar.
sc = m.scatter(x,y, s=z, zorder=4, cmap=cmap, c=mags)
plt.colorbar(sc, orientation='horizontal')

#Title
r = plt.title(title)

#Day/night shading
class DateWrapper(object):
    """
    This class is a hack-- basemap is expecting a UTC time to have `None` as the utcoffset attrib.
    """
    def __init__(self, dt):
        self._dt = dt

    def __getattr__(self, x):
        return getattr(self._dt, x)

    def utcoffset(self):
        return None
date = DateWrapper(datetime.datetime.today().replace(tzinfo=tz_local).astimezone(tz_utc))
CS=m.nightshade(date)

#Tight layout
fig.tight_layout()
Posted in Technology | Tagged , | Leave a comment

ITS Coffee Break #78: My Lafayette, iOS 7, Wearable Computing, MOOCs

n Episode #78 we discuss the launch of the My Lafayette campus portal, the 802.1x pilot in Skillman Library and the upcoming Moodle Hack/Doc Fest at Eckerd College in January 2014.  In Tech News we look at the launch of iOS 7 and the iPhone 5S/C, a new version of Microsoft’s Surface tablet, and new entries into the wearable computing marketplace. We close out the podcast discussing ”MOOCs: In Theory and In Practice”, an upcoming lecture by Prof. Robert Ghrist of University of Pennsylvania that’s sponsored by the Center for the Integration of Teaching, Learning, and Scholarship.

 

Posted in Podcast | Tagged | Leave a comment

Summer Coffee Breaks

Courtney Bentley and Ken Newquist released three new ITS Coffee Breaks over the summer:

Posted in Uncategorized | Tagged | Leave a comment