# ============================================================
# File:colorMap.py
# Description: program for making a color map of data
#              plotted on US map
# Author: Emily G. Allen
# Date: April 2008
# ============================================================  

# ============================================================
# Required Files
# ============================================================    
from state import *
from graphics import *
from math import *

# ============================================================
# ============================================================
# Creating/Drawing Colorized Map of US
# ============================================================
# ============================================================

# ============================================================
# Function: colorMap
# Description: creates a color map based upon range of data
#              within limits from minColor to maxColor
# Inputs: minColor (low values), maxColor (high values), data
#         minColor and maxColor are lists of [red, blue, green]
#         values
# Outputs: a color_rgb equivalency for each value in data
# ============================================================
def colorMap(minColor, maxColor, data):
        
    # calculate range of the data
    rnge = float(max(data) - min(data)) # to insure floating pt division later on
    if rnge == 0: # account for only one value
        rnge = 1

    # iterate over values in the data
    # calculating color equivalency for each value
    # equivalency is proportional to relative distance betweeen value and min(data)
    # minColor = [red, blue, green]
    # maxColor = [red, blue, green]
    colorList = []
    for value in data:
        difference = (value - min(data)) / rnge
        red = (maxColor[0] - minColor[0]) * difference + minColor[0]
        green = (maxColor[1] - minColor[1]) * difference + minColor[1]
        blue = (maxColor[2] - minColor[2]) * difference + minColor[2]

        colorList.append(color_rgb(red, green, blue)) # add rgb_color to list

    return colorList

# ============================================================
# Function: drawMap
# Description: draws (colorized) map of US
# Inputs: the drawing window, the statelist, the color list
# Outputs: none
# ============================================================  
def drawMap(window, stateList, capital, colorList):

    if len(colorList) == 0: # if no color, draw states in white
        for state in stateList:
            state.draw(window)
    else: # otherwise draw colored states
        for i in range(len(stateList)):
            stateList[i].draw(window, colorList[i])

    if capital: # if capital is True place capital dots
        for state in stateList:
            state.drawCapital(window)
            
# ============================================================
# Function: clearMap
# Description: clears the map to be redrawn
# Inputs: stateList
# Outputs: none
# ============================================================
def clearMap(stateList, window):
    for state in stateList:
        state.undraw()

    # erase legend
    rect = Rectangle(Point(100, 1100), Point(1400, 900))
    rect.setFill('white')
    rect.setOutline('white')
    rect.draw(window)

    # erase title
    rect = Rectangle(Point(0, 200), Point(1500,  0))
    rect.setFill('white')
    rect.setOutline('white')
    rect.draw(window)
        
# ============================================================
# Function: drawLegend
# Description: draws the colorbar legend
# Inputs: minColor, maxColor from which colormap was generated,
#         the data, categories, and the drawing window
# Outputs: none
# ============================================================  
def drawLegend(minColor, maxColor, data, categories, window):

    # determine the range being represented
    if categories == -1:
        increment =  range(int(min(data)), int(max(data)), int((max(data) - min(data)) / 10))
    else:
        increment = range(len(categories))

    # generated a color map for the legend out of the represented range
    legend = colorMap(minColor, maxColor, increment)

    # draw one rectangle for each color in legend
    x1, x2, y1, y2 = 100, 150, 1100, 1050 # initial rect. coords
    for c in range(len(legend)):
        r = Rectangle(Point(x1, y1), Point(x2, y2))
        r.setFill(legend[c])
        r.draw(window)

        # label the rectangle
        if categories == -1: # if quantitative values
            t = str(increment[c])
        else: 
            t = categories[c]  
        label = Text(Point(x1 + 25, y2 - 50), t)
        label.draw(window)
        
        x1 = x1 + 100 #update coordinates
        x2 = x2 + 100
          
# ============================================================
# ============================================================
# Processing Data to Be Plotted
# ============================================================
# ============================================================

# ============================================================
# Function: loadData
# Description: loads a list of data to be plotted
# Inputs: stateList (data from file; in format, state, data)
#         first line indicates whether or not data are quantitative
#         if qualitative, list number of categories
# Outputs: list of data
# ============================================================
def loadData(stateList):
    print '======================================================'
    print 'YOU HAVE ELECTED TO LOAD DATA FROM A FILE'
    print '======================================================\n'
    print 'Make sure your data file is in the following format: '
    print 'DATATYPE'
    print 'Map Title'
    print 'State Name1, Value1'
    print 'State Name2, Value2'
    print '...\n'
    print 'DATATYPE should be either QUANT (quantitative) or'
    print 'CAT (qualitative or categories).  If DATATYPE is CAT'
    print 'follow specifier with list of categories, e.g., :'
    print '     CAT=Bush, Kerry\n'
    print 'See \'election2004.txt\' for example.\n\n'

    # get file name
    filename = raw_input('Please enter data file name: ')
    infile = open(filename, 'r')

    # figure out the data type
    datatype = infile.readline()
    if datatype.find('CAT') != -1: # if qualitative
        # determine the categories and assign #s
        datatype = datatype.split('=') # segregate category list 
        categories = datatype[1].split(',') # segregate categories
        for c in range(len(categories)): # clean up string (remove spaces and newlines
            categories[c] = categories[c].strip()

    else: # otherwise quantitative
        categories = -1
        
    # read in title
    title = infile.readline()
    
    # read in remainder of data
    data = []
    for line in infile:
        line = line.split(',')
        stateName = line[0] # first thing is state name
        value = line[1] # second thing on line is the value

        # find the right state and associate value
        for state in stateList:
            if stateName == state.getName():
                if categories == -1:
                    # quantitative, evaluate value and
                    # associate with the state
                    state.setValue(eval(value))
                else:
                    # qualitative, find the index of the value
                    # in the category list and associate that
                    # with the state
                    state.setValue(categories.index(value.strip()))   

    infile.close()
    return title, categories, stateList

# ============================================================
# Function: loadEnergyData
# Description: loads energy data and makes available for plotting
# Inputs: none
# Outputs: set of lists of energy data, titles
# ============================================================        
def loadEnergyData():
    infile = open('stateEnergy.txt', 'r')

    # column titles
    titles = infile.readline()
    titles = titles.split(',')

    data = []
    # resut of data
    for line in infile:
        data.append(line.split(','))

    return titles, data
        
# ============================================================
# Function: setEnergyData
# Description: stores appropriate energy data subset in states
# Inputs: selected data to store, energy data table, state list
# Outputs: state list
# ============================================================  
def setEnergyData(choice, data, stateList):
    for currentState in data:
        # iterate thru states
        for state in stateList:
            # find saved state with same name
            if currentState[0] == state.getName():
                state.setValue(eval(currentState[choice]))
    return stateList

# ============================================================
# ============================================================
# Main Program
# ============================================================
# ============================================================         
def main():
    # create drawing window
    win = GraphWin('States', 1000, 700)
    win.setCoords(0, 1200, 1500 ,0)
    win.setBackground('white')# draw the map and legend 
  
    # load state polygons and draw initial map
    states = loadStateData()
    drawMap(win, states, False, [])

    # load energy file into menu
    energyTitles, energyData = loadEnergyData()
  
    # as long as user wants to update plot
    plotAgain = 'Y'
    while plotAgain == 'Y': #
        # create menu to choose personal data file or energy file
        print '\n\n======================================================'
        print 'SELECT DATA'
        print '======================================================'
        print 'Energy Data Sets: '
        i = 1
        while i < len(energyTitles):      
            if i == 2:
                print '\nPercent Total Energy Consumption per Source\n'
            if i == 9:
                print '\nPercent Total Energy Consumption per Consumer\n'
                
            print '[%d]: %s' % (i, energyTitles[i])
            i = i + 1
            
        print '\n[%d]: Load Data File' % i
        print '======================================================'

        dataChoice = input('Enter choice (1 - %d): ' % len(energyTitles))
        while dataChoice > len(energyTitles):
              dataChoice = input('Please enter a value in the correct range: ')

        # select data to be plotted
        if dataChoice == len(energyTitles): # load other data
            title, categories, states = loadData(states)
        else: # energy data
            categories = -1
            if dataChoice <= 8:
                title = 'Source: '
            else:
                title = 'Consumer: '
            title = title + energyTitles[dataChoice] + ' Consumption (BTU)'
            states = setEnergyData(dataChoice, energyData, states)

        print '\n\n======================================================'
        print 'Please enter lower and upper values for color map.'
        print 'For example, to make a scale that maps from blue to red: '
        print 'minColor = [0, 50, 255]'
        print 'maxColor = [255, 50, 0]'
        print '======================================================s'
        minColor = input('Please enter minColor [r, g, b]: ')
        maxColor = input('Please enter maxColor [r, g, b]: ')

        # generate color map
        colors = colorMap(minColor, maxColor, getStateData(states))
           
        clearMap(states, win) # clear items
        drawMap(win, states, False, colors)
        drawLegend(minColor, maxColor, getStateData(states), categories, win)

        # draw Title
        titleText = Text(Point(750, 100), title)
        titleText.draw(win)

        print '\n\n======================================================'
        plotAgain = raw_input('Generate another plot (Y/N)? ')
        plotAgain = plotAgain[0].upper() # deal with y/Y yes/Yes
                              
main()
