<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __BEGIN_LICENSE__
#Copyright (c) 2015, United States Government, as represented by the
#Administrator of the National Aeronautics and Space Administration.
#All rights reserved.
#
#The xGDS platform is licensed under the Apache License, Version 2.0
#(the "License"); you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#http://www.apache.org/licenses/LICENSE-2.0.
#
#Unless required by applicable law or agreed to in writing, software distributed
#under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#CONDITIONS OF ANY KIND, either express or implied. See the License for the
#specific language governing permissions and limitations under the License.
# __END_LICENSE__

# This Python script will generate a KML network link and a KML file which renders
# placemarks for the air quality at Hawai'i Volcanoes National Park.

import requests
import json
from xml.sax.saxutils import escape
from fastkml import kml, geometry, styles
from shapely.geometry import Point, Polygon
from polycircles import polycircles
from LatLon import LatLon

USE_MEMCACHE = True
if USE_MEMCACHE:
    import memcache
    _cache = memcache.Client(['127.0.0.1:11211'], debug=0)

SERVER_URL = None
RAW_DATA_URL = "http://www.hawaiiso2network.com/havo_json.txt" # source of the json for the data
ADVISORY_SUMMARY_URL = 'http://www.hawaiiso2network.com/summary.html'

# Update this with the path to icons on your server
ICON_PATH = '/static/basaltApp/icons/hvnp_air_quality'

NAME_SPACE = '{http://www.opengis.net/kml/2.2}'

# Global variables
CURRENT_DATA = None
CONDITIONS = {}
SITE_POSITIONS = {'Visitor Center': {'lat': 19.4308,
                                     'lon': -155.2578,
                                     'alt': 1215},
                    'Jaggar Museum': {'lat': 19.4203,
                                    'lon': -155.2881,
                                    'alt': 1123}}

WEDGE_POSITIONS = {"Pu'U O'o Crater": {'lat': 19.387979,
                                       'lon': -155.107504,
                                       'alt': 1215},
                  'Halemaumau Crater': {'lat': 19.410000,
                                        'lon': -155.286389,
                                        'alt': 1123}}

PLUME_SOURCE = 'Jaggar Museum'  # update to which is the source of the plume data

#KML colors are aabbggrr in hex binary; colors in this map do not include transparency
ADVISORY_SCALE = {'Unknown':'FFFFFF',
                  'Good':'57C700',
                  'Moderate':'00DEFF',
                  'Unhealthy for Sensitive Groups':'318FF5',
                  'Unhealthy':'2601FF',
                  'Very Unhealthy':'480094',
                  'Hazardous':'260D80'}

WEDGE_LENGTH = 50.0 # in km
WEDGE_ANGLE = 30 # in degrees
CIRCLE_RADIUS = 2.0 #in km
DEGREE_SYMBOL = unichr(176)

def getCurrentStateKml(serverUrl):
    ''' Create the overall KML for the current state of the air quality.
    If we can't get fresh data, return None
    serverUrl = the url of the server providing this KML, so we can have the network link and the icons.
    '''

    # Try to get data
    global CURRENT_DATA
    CURRENT_DATA = getData()
    if not CURRENT_DATA:
        return None

    global SERVER_URL
    SERVER_URL = serverUrl

    # Create the root KML object
    topKml = kml.KML()

    # Create a KML Document and add it to the KML root object
    document = kml.Document(NAME_SPACE, 'hvnp_caq_doc',
                            "Hawai'i Volcanoes National Park",
                            "Approximate direction of the volcanic gas plumes (wedges) from Halema'uma'u and Pu'u 'O'o. Colored circles show the current air quality conditions for sulfur dioxide and particulate matter at each monitoring site.",
                            styles=buildStyles())

    topKml.append(document)

    # Read and interpret the current conditions
    getConditions()

    # Build a top level KML folder
    topFolder = kml.Folder(NAME_SPACE, 'hvnp_caq_doc', 'Current Air Quality', getConditionsString())

    # Build placemarks for all the found sites
    buildSites(topFolder)

    # Put the document together
    document.append(topFolder)

    # Return the KML as a string.
    return document.to_string(prettyprint=True)

def buildNetworkLink(url, name, interval=900):
    ''' Build a Network link to refresh an url every interval seconds
    '''
    return '''
<NetworkLink>
  <name>%(name)s</name>
  <Link>
    <href>%(url)s</href>
    <refreshMode>onInterval</refreshMode>
    <refreshInterval>%(interval)d</refreshInterval>
  </Link>
</NetworkLink>
''' % dict(name=escape(name),
           interval=interval,
           url=escape(url))

def getData():
    ''' Get the json data from the hawaiiso2network website and store it
    '''
    try:
        request = requests.get(RAW_DATA_URL, timeout=30)
        return request.json()
#         response = urllib2.urlopen(RAW_DATA_URL)
#         return json.load(response)
    except:
        return None

def getDataValue(keys, dictionary=None):
    ''' Convenience method to get data out of the json '''
    global CURRENT_DATA
    if dictionary is None:
        dictionary = CURRENT_DATA
    if "." in keys:
        key, rest = keys.split(".", 1)
        return getDataValue(rest, dictionary[key])
    else:
        if keys in dictionary:
            return dictionary[keys]
        return None

def getPlumeSourceData():
    ''' Get the data map for the current plume source '''
    for key in CURRENT_DATA:
        data = CURRENT_DATA[key]
        if data['name'] == PLUME_SOURCE:
            return data

def getConditions():
    ''' Interpret the MET block '''
    met = getDataValue('MET')
    CONDITIONS['WIND_SPEED'] = int(met['WS'])
    CONDITIONS['WIND_SPEED_METRIC'] = int(met['WS-metric'])
    CONDITIONS['WIND_DIRECTION'] = int(met['WD']) - 180
    CONDITIONS['TEMPERATURE'] = int(met['AT'])
    CONDITIONS['TEMPERATURE_METRIC'] = int(met['AT-metric'])
    CONDITIONS['LOW_WIND'] = CONDITIONS['WIND_SPEED_METRIC'] <= int(met['WS-threshold'])
    CONDITIONS['HUMIDITY'] = int(met['RH'])
    CONDITIONS['PARKWIDE'] = {'SO2': getDataValue('PARKWIDE.SO2.AQItext'),
                              'PM25': getDataValue('PARKWIDE.PM25.AQItext')}

def getConditionsString():
    ''' Get printable string of the high level conditions '''
    result = ('%s\nWind Speed: %d mph (%d m/s)\nWind Direction: %d\nTemperature: %d %sF (%d %sC)\nHumidity: %d %%\nParkwide SO2: %s\nParkwide PM25: %s')\
        % (getDataValue('datadate'),
           CONDITIONS['WIND_SPEED'],
           CONDITIONS['WIND_SPEED_METRIC'],
           CONDITIONS['WIND_DIRECTION'],
           CONDITIONS['TEMPERATURE'],
           DEGREE_SYMBOL,
           CONDITIONS['TEMPERATURE_METRIC'],
           DEGREE_SYMBOL,
           CONDITIONS['HUMIDITY'],
           CONDITIONS['PARKWIDE']['SO2'],
           CONDITIONS['PARKWIDE']['PM25']
           )
    return result

def makeKey(word):
    return word.replace(' ','_').lower()

def getDotIconId(level):
    ''' Get the clean id for a dot icon and style '''
    if level not in ADVISORY_SCALE:
        level = 'Unknown'
    return makeKey(level) + '_dot'

def buildDotIconUrl(level):
    ''' Build the URL to get the image for a dot icon '''
    return ('%s%s/%s.png') % (SERVER_URL, ICON_PATH, getDotIconId(level))

def getPolyStyleId(level):
    if level not in ADVISORY_SCALE:
        level = 'Unknown'
    ''' Get the clean id for a polygon style '''
    return makeKey(level) + '_poly'

def buildStyles():
    ''' Build the various styles to be used for the placemarks'''
    kmlStyles = []
    for level in ADVISORY_SCALE:
        # build the dot style
        innerIconStyle = styles.IconStyle(NAME_SPACE, scale=.7, icon_href=buildDotIconUrl(level))
        iconStyle = styles.Style(NAME_SPACE, id=getDotIconId(level), styles=[innerIconStyle])
        kmlStyles.append(iconStyle)

        # build the polygon style
        innerPolyStyle = styles.PolyStyle(NAME_SPACE, color='33' + ADVISORY_SCALE[level], fill=1, outline=1)
        innerLineStyle = styles.LineStyle(NAME_SPACE, color='FF' + ADVISORY_SCALE[level], width=2)
        polyStyle = styles.Style(NAME_SPACE, id=getPolyStyleId(level), styles=[innerLineStyle, innerPolyStyle])
        kmlStyles.append(polyStyle)
    return kmlStyles

def buildSites(folder):
    ''' Iterate through the json data and create visible KML elements for each site.
    '''
    siteData = 'start'
    index = 1
    while siteData is not None:
        key = 'SITE' + str(index)
        siteData = getDataValue(key)
        if siteData:
            buildPlacemarkForSite(folder, siteData)
        index += 1

def buildPlacemarkForSite(folder, siteData):
    ''' Build a placemark for a particular site.
        If the site is in PLUME_SOURCES, then show a wedge or circle depending on the wind speed.
        Otherwise show just a point.
        Render a popup or not based on json data.
    '''
    siteName = siteData['name']
    if siteName in SITE_POSITIONS:
        dotPlacemark = kml.Placemark(NAME_SPACE, siteName, siteName, getSiteDescription(siteData))
        dotPlacemark.styleUrl = '#' + getDotIconId(getDataValue('SO2.AQItext', siteData))
        dotPlacemark.geometry = buildPoint(SITE_POSITIONS[siteName], siteName)
        folder.append(dotPlacemark)

        if siteName == PLUME_SOURCE:
            plumeData = getPlumeSourceData()
            for crater in WEDGE_POSITIONS:
                craterPlacemark = kml.Placemark(NAME_SPACE, makeKey(crater), crater, getCraterDescription(plumeData))
                craterPlacemark.styleUrl = '#' + getPolyStyleId(getDataValue('SO2.AQItext', plumeData))
                if CONDITIONS['LOW_WIND']:
                    craterPlacemark.geometry = buildCircle(WEDGE_POSITIONS[crater], crater)
                else:
                    craterPlacemark.geometry = buildWedge(WEDGE_POSITIONS[crater], CONDITIONS['WIND_DIRECTION'], crater)
                folder.append(craterPlacemark);

def getSiteDescription(siteData):
    ''' Get printable string to show when the user clicks on a placemark for a site '''
    result = ('%s\n') % getDataValue('name', siteData)
    if 'SO2' in siteData:
        result += ('SO2: %f ppm\n') % float(getDataValue('SO2.average15', siteData))
    if 'PM25' in siteData:
        result += ('PM25: %d %sg/m3\n') % (int(getDataValue('PM25.average15', siteData)), unichr(956))
    return result

def getCraterDescription(siteData):
    ''' Get printable string of the plume conditions '''
    result = ('%s\nWind Speed: %d mph (%d m/s)\nWind Direction: %d\nTemperature: %d %sF (%d %sC)\nHumidity: %d %%\n%s SO2: %s ppm (%s)\n%s PM25: %s %sg/m3 (%s)')\
        % (getDataValue('datadate'),
           CONDITIONS['WIND_SPEED'],
           CONDITIONS['WIND_SPEED_METRIC'],
           CONDITIONS['WIND_DIRECTION'],
           CONDITIONS['TEMPERATURE'],
           DEGREE_SYMBOL,
           CONDITIONS['TEMPERATURE_METRIC'],
           DEGREE_SYMBOL,
           CONDITIONS['HUMIDITY'],
           PLUME_SOURCE,
           siteData['SO2']['average15'],
           siteData['SO2']['AQItext'],
           PLUME_SOURCE,
           siteData['PM25']['average15'],
           unichr(956),
           siteData['PM25']['AQItext'],
           )
    return result

def buildPoint(location, name):
    pointID = makeKey(name) + '_point'
    pointShape =  Point([(location['lon'], location['lat'], location['alt'])])
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=pointID, geometry=pointShape, extrude=False, tessellate=False, altitude_mode='clampToGround')
    return kmlGeometry

def buildHeading(heading):
    if heading < 0:
        heading += 360
    elif heading > 360:
        heading -= 360
    return heading

def buildWedge(location, heading, name):
    wedgeID = makeKey(name) + '_wedge'
    centerPoint = LatLon(location['lat'], location['lon'])
    point1 = centerPoint.offset(buildHeading(heading - (WEDGE_ANGLE/2.0)), float(WEDGE_LENGTH))
    point2 = centerPoint.offset(buildHeading(heading + (WEDGE_ANGLE/2.0)), float(WEDGE_LENGTH))
    coords = [(centerPoint.lon.decimal_degree, centerPoint.lat.decimal_degree),
              (point1.lon.decimal_degree, point1.lat.decimal_degree),
              (point2.lon.decimal_degree, point2.lat.decimal_degree)]
    wedgeShape = Polygon(coords)
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=wedgeID, geometry=wedgeShape, extrude=False, tessellate=False, altitude_mode='clampToGround')
    return kmlGeometry

def buildCircle(location, name):
    circleID = makeKey(name) + '_circle'
    boundaryCircle = polycircles.Polycircle(latitude=location['lat'],
                                            longitude=location['lon'],
                                            radius=CIRCLE_RADIUS * 1000,
                                            number_of_vertices=36)
    coords = [(coord[1], coord[0]) for coord in boundaryCircle.vertices]
    circleShape = Polygon(coords)
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=circleID, geometry=circleShape, extrude=False, tessellate=True, altitude_mode='clampToGround')
    return kmlGeometry
<commit_msg>hvnp kml uses cache now<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __BEGIN_LICENSE__
#Copyright (c) 2015, United States Government, as represented by the
#Administrator of the National Aeronautics and Space Administration.
#All rights reserved.
#
#The xGDS platform is licensed under the Apache License, Version 2.0
#(the "License"); you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#http://www.apache.org/licenses/LICENSE-2.0.
#
#Unless required by applicable law or agreed to in writing, software distributed
#under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#CONDITIONS OF ANY KIND, either express or implied. See the License for the
#specific language governing permissions and limitations under the License.
# __END_LICENSE__

# This Python script will generate a KML network link and a KML file which renders
# placemarks for the air quality at Hawai'i Volcanoes National Park.

import datetime
import pytz
import requests
import json
from xml.sax.saxutils import escape
from fastkml import kml, geometry, styles
from shapely.geometry import Point, Polygon
from polycircles import polycircles
from LatLon import LatLon

USE_MEMCACHE = True
CACHE_KEY = 'HVNP_AIR'

SERVER_URL = None
RAW_DATA_URL = "http://www.hawaiiso2network.com/havo_json.txt" # source of the json for the data
ADVISORY_SUMMARY_URL = 'http://www.hawaiiso2network.com/summary.html'

# Update this with the path to icons on your server
ICON_PATH = '/static/basaltApp/icons/hvnp_air_quality'

NAME_SPACE = '{http://www.opengis.net/kml/2.2}'

# Global variables
CURRENT_DATA = None
CONDITIONS = {}
SITE_POSITIONS = {'Visitor Center': {'lat': 19.4308,
                                     'lon': -155.2578,
                                     'alt': 1215},
                    'Jaggar Museum': {'lat': 19.4203,
                                    'lon': -155.2881,
                                    'alt': 1123}}

WEDGE_POSITIONS = {"Pu'U O'o Crater": {'lat': 19.387979,
                                       'lon': -155.107504,
                                       'alt': 1215},
                  'Halemaumau Crater': {'lat': 19.410000,
                                        'lon': -155.286389,
                                        'alt': 1123}}

PLUME_SOURCE = 'Jaggar Museum'  # update to which is the source of the plume data

#KML colors are aabbggrr in hex binary; colors in this map do not include transparency
ADVISORY_SCALE = {'Unknown':'FFFFFF',
                  'Good':'57C700',
                  'Moderate':'00DEFF',
                  'Unhealthy for Sensitive Groups':'318FF5',
                  'Unhealthy':'2601FF',
                  'Very Unhealthy':'480094',
                  'Hazardous':'260D80'}

WEDGE_LENGTH = 50.0 # in km
WEDGE_ANGLE = 30 # in degrees
CIRCLE_RADIUS = 2.0 #in km
DEGREE_SYMBOL = unichr(176)

def getCurrentStateKml(serverUrl):
    ''' Create the overall KML for the current state of the air quality.
    If we can't get fresh data, return None
    serverUrl = the url of the server providing this KML, so we can have the network link and the icons.
    '''

    # Try to get data
    global CURRENT_DATA
    CURRENT_DATA = getData()
    if not CURRENT_DATA:
        return None

    global SERVER_URL
    SERVER_URL = serverUrl

    # Create the root KML object
    topKml = kml.KML()

    # Create a KML Document and add it to the KML root object
    document = kml.Document(NAME_SPACE, 'hvnp_caq_doc',
                            "Hawai'i Volcanoes National Park",
                            "Approximate direction of the volcanic gas plumes (wedges) from Halema'uma'u and Pu'u 'O'o. Colored circles show the current air quality conditions for sulfur dioxide and particulate matter at each monitoring site.",
                            styles=buildStyles())

    topKml.append(document)

    # Read and interpret the current conditions
    getConditions()

    # Build a top level KML folder
    topFolder = kml.Folder(NAME_SPACE, 'hvnp_caq_doc', 'Current Air Quality', getConditionsString())

    # Build placemarks for all the found sites
    buildSites(topFolder)

    # Put the document together
    document.append(topFolder)

    # Return the KML as a string.
    return document.to_string(prettyprint=True)

def buildNetworkLink(url, name, interval=900):
    ''' Build a Network link to refresh an url every interval seconds
    '''
    return '''
<NetworkLink>
  <name>%(name)s</name>
  <Link>
    <href>%(url)s</href>
    <refreshMode>onInterval</refreshMode>
    <refreshInterval>%(interval)d</refreshInterval>
  </Link>
</NetworkLink>
''' % dict(name=escape(name),
           interval=interval,
           url=escape(url))

def getRawData():
    ''' Get the json data from the hawaiiso2network website and store it
    '''
    try:
        request = requests.get(RAW_DATA_URL, timeout=30)
        return request.json()
    except:
        return None

def getCachedMinutesAgo(datadate):
    try:
        hst = pytz.timezone('US/Hawaii')
        splits = datadate.split()
        lasttime = datetime.datetime.strptime(splits[1] + ' ' + splits[2], '%m/%d/%Y %H:%M')
        lasttime = hst.localize(lasttime)
        now = datetime.datetime.now(hst)
        delta = now - lasttime
        return delta.seconds / 60
    except:
        return 60 #force refresh

def getData():
    ''' Get the json data from cache or raw.
    '''
    if USE_MEMCACHE:
        import memcache
        _cache = memcache.Client(['127.0.0.1:11211'], debug=0)
        cachedData = _cache.get(CACHE_KEY)
        # If there is no cached data at all
        if not cachedData:
            newData = getRawData()
            if newData:
                _cache.set(CACHE_KEY, newData)
            return newData
        else:
            ago = getCachedMinutesAgo(cachedData['datadate'])
            if ago > 15:
                newData = getRawData()
                if newData:
                    _cache.set(CACHE_KEY, newData)
                    return newData
                else:
                    if ago > 30:
                        _cache.set(CACHE_KEY, None)
                        return None
            return cachedData
    else:
        return getRawData()

def getDataValue(keys, dictionary=None):
    ''' Convenience method to get data out of the json '''
    global CURRENT_DATA
    if dictionary is None:
        dictionary = CURRENT_DATA
    if "." in keys:
        key, rest = keys.split(".", 1)
        return getDataValue(rest, dictionary[key])
    else:
        if keys in dictionary:
            return dictionary[keys]
        return None

def getPlumeSourceData():
    ''' Get the data map for the current plume source '''
    for key in CURRENT_DATA:
        data = CURRENT_DATA[key]
        if data['name'] == PLUME_SOURCE:
            return data

def getConditions():
    ''' Interpret the MET block '''
    met = getDataValue('MET')
    CONDITIONS['WIND_SPEED'] = int(met['WS'])
    CONDITIONS['WIND_SPEED_METRIC'] = int(met['WS-metric'])
    CONDITIONS['WIND_DIRECTION'] = int(met['WD']) - 180
    CONDITIONS['TEMPERATURE'] = int(met['AT'])
    CONDITIONS['TEMPERATURE_METRIC'] = int(met['AT-metric'])
    CONDITIONS['LOW_WIND'] = CONDITIONS['WIND_SPEED_METRIC'] <= int(met['WS-threshold'])
    CONDITIONS['HUMIDITY'] = int(met['RH'])
    CONDITIONS['PARKWIDE'] = {'SO2': getDataValue('PARKWIDE.SO2.AQItext'),
                              'PM25': getDataValue('PARKWIDE.PM25.AQItext')}

def getConditionsString():
    ''' Get printable string of the high level conditions '''
    result = ('%s\nWind Speed: %d mph (%d m/s)\nWind Direction: %d\nTemperature: %d %sF (%d %sC)\nHumidity: %d %%\nParkwide SO2: %s\nParkwide PM25: %s')\
        % (getDataValue('datadate'),
           CONDITIONS['WIND_SPEED'],
           CONDITIONS['WIND_SPEED_METRIC'],
           CONDITIONS['WIND_DIRECTION'],
           CONDITIONS['TEMPERATURE'],
           DEGREE_SYMBOL,
           CONDITIONS['TEMPERATURE_METRIC'],
           DEGREE_SYMBOL,
           CONDITIONS['HUMIDITY'],
           CONDITIONS['PARKWIDE']['SO2'],
           CONDITIONS['PARKWIDE']['PM25']
           )
    return result

def makeKey(word):
    return word.replace(' ','_').lower()

def getDotIconId(level):
    ''' Get the clean id for a dot icon and style '''
    if level not in ADVISORY_SCALE:
        level = 'Unknown'
    return makeKey(level) + '_dot'

def buildDotIconUrl(level):
    ''' Build the URL to get the image for a dot icon '''
    return ('%s%s/%s.png') % (SERVER_URL, ICON_PATH, getDotIconId(level))

def getPolyStyleId(level):
    if level not in ADVISORY_SCALE:
        level = 'Unknown'
    ''' Get the clean id for a polygon style '''
    return makeKey(level) + '_poly'

def buildStyles():
    ''' Build the various styles to be used for the placemarks'''
    kmlStyles = []
    for level in ADVISORY_SCALE:
        # build the dot style
        innerIconStyle = styles.IconStyle(NAME_SPACE, scale=.7, icon_href=buildDotIconUrl(level))
        iconStyle = styles.Style(NAME_SPACE, id=getDotIconId(level), styles=[innerIconStyle])
        kmlStyles.append(iconStyle)

        # build the polygon style
        innerPolyStyle = styles.PolyStyle(NAME_SPACE, color='33' + ADVISORY_SCALE[level], fill=1, outline=1)
        innerLineStyle = styles.LineStyle(NAME_SPACE, color='FF' + ADVISORY_SCALE[level], width=2)
        polyStyle = styles.Style(NAME_SPACE, id=getPolyStyleId(level), styles=[innerLineStyle, innerPolyStyle])
        kmlStyles.append(polyStyle)
    return kmlStyles

def buildSites(folder):
    ''' Iterate through the json data and create visible KML elements for each site.
    '''
    siteData = 'start'
    index = 1
    while siteData is not None:
        key = 'SITE' + str(index)
        siteData = getDataValue(key)
        if siteData:
            buildPlacemarkForSite(folder, siteData)
        index += 1

def buildPlacemarkForSite(folder, siteData):
    ''' Build a placemark for a particular site.
        If the site is in PLUME_SOURCES, then show a wedge or circle depending on the wind speed.
        Otherwise show just a point.
        Render a popup or not based on json data.
    '''
    siteName = siteData['name']
    if siteName in SITE_POSITIONS:
        dotPlacemark = kml.Placemark(NAME_SPACE, siteName, siteName, getSiteDescription(siteData))
        dotPlacemark.styleUrl = '#' + getDotIconId(getDataValue('SO2.AQItext', siteData))
        dotPlacemark.geometry = buildPoint(SITE_POSITIONS[siteName], siteName)
        folder.append(dotPlacemark)

        if siteName == PLUME_SOURCE:
            plumeData = getPlumeSourceData()
            for crater in WEDGE_POSITIONS:
                craterPlacemark = kml.Placemark(NAME_SPACE, makeKey(crater), crater, getCraterDescription(plumeData))
                craterPlacemark.styleUrl = '#' + getPolyStyleId(getDataValue('SO2.AQItext', plumeData))
                if CONDITIONS['LOW_WIND']:
                    craterPlacemark.geometry = buildCircle(WEDGE_POSITIONS[crater], crater)
                else:
                    craterPlacemark.geometry = buildWedge(WEDGE_POSITIONS[crater], CONDITIONS['WIND_DIRECTION'], crater)
                folder.append(craterPlacemark);

def getSiteDescription(siteData):
    ''' Get printable string to show when the user clicks on a placemark for a site '''
    result = ('%s\n') % getDataValue('name', siteData)
    if 'SO2' in siteData:
        result += ('SO2: %f ppm\n') % float(getDataValue('SO2.average15', siteData))
    if 'PM25' in siteData:
        result += ('PM25: %d %sg/m3\n') % (int(getDataValue('PM25.average15', siteData)), unichr(956))
    return result

def getCraterDescription(siteData):
    ''' Get printable string of the plume conditions '''
    result = ('%s\nWind Speed: %d mph (%d m/s)\nWind Direction: %d\nTemperature: %d %sF (%d %sC)\nHumidity: %d %%\n%s SO2: %s ppm (%s)\n%s PM25: %s %sg/m3 (%s)')\
        % (getDataValue('datadate'),
           CONDITIONS['WIND_SPEED'],
           CONDITIONS['WIND_SPEED_METRIC'],
           CONDITIONS['WIND_DIRECTION'],
           CONDITIONS['TEMPERATURE'],
           DEGREE_SYMBOL,
           CONDITIONS['TEMPERATURE_METRIC'],
           DEGREE_SYMBOL,
           CONDITIONS['HUMIDITY'],
           PLUME_SOURCE,
           siteData['SO2']['average15'],
           siteData['SO2']['AQItext'],
           PLUME_SOURCE,
           siteData['PM25']['average15'],
           unichr(956),
           siteData['PM25']['AQItext'],
           )
    return result

def buildPoint(location, name):
    pointID = makeKey(name) + '_point'
    pointShape =  Point([(location['lon'], location['lat'], location['alt'])])
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=pointID, geometry=pointShape, extrude=False, tessellate=False, altitude_mode='clampToGround')
    return kmlGeometry

def buildHeading(heading):
    if heading < 0:
        heading += 360
    elif heading > 360:
        heading -= 360
    return heading

def buildWedge(location, heading, name):
    wedgeID = makeKey(name) + '_wedge'
    centerPoint = LatLon(location['lat'], location['lon'])
    point1 = centerPoint.offset(buildHeading(heading - (WEDGE_ANGLE/2.0)), float(WEDGE_LENGTH))
    point2 = centerPoint.offset(buildHeading(heading + (WEDGE_ANGLE/2.0)), float(WEDGE_LENGTH))
    coords = [(centerPoint.lon.decimal_degree, centerPoint.lat.decimal_degree),
              (point1.lon.decimal_degree, point1.lat.decimal_degree),
              (point2.lon.decimal_degree, point2.lat.decimal_degree)]
    wedgeShape = Polygon(coords)
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=wedgeID, geometry=wedgeShape, extrude=False, tessellate=False, altitude_mode='clampToGround')
    return kmlGeometry

def buildCircle(location, name):
    circleID = makeKey(name) + '_circle'
    boundaryCircle = polycircles.Polycircle(latitude=location['lat'],
                                            longitude=location['lon'],
                                            radius=CIRCLE_RADIUS * 1000,
                                            number_of_vertices=36)
    coords = [(coord[1], coord[0]) for coord in boundaryCircle.vertices]
    circleShape = Polygon(coords)
    kmlGeometry = geometry.Geometry(NAME_SPACE, id=circleID, geometry=circleShape, extrude=False, tessellate=True, altitude_mode='clampToGround')
    return kmlGeometry
<|endoftext|>


<commit_before>"""
Primary energy and CO2 emissions model algorithm for mobility.
"""

from __future__ import division

import os
import pandas as pd
from geopandas import GeoDataFrame as gpdf
import cea.inputlocator

__author__ = "Martin Mosteiro Romero"
__copyright__ = "Copyright 2016, Architecture and Building Systems - ETH Zurich"
__credits__ = ["Martin Mosteiro Romero"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daren Thomas"
__email__ = "cea@arch.ethz.ch"
__status__ = "Production"

def lca_mobility(locator, config):
    """
    Calculation of the primary energy and CO2 emissions for mobility in the area based on the present day values
    calculated for the 2000 Watt society target.

    The current values for the Swiss case for each type of occupancy are based on the following sources:

    - [SIA_2040_2011]_: 'MULTI_RES', 'SINGLE_RES', 'SCHOOL', 'OFFICE'
    - [BFE_2012]_: 'HOTEL', 'RETAIL', 'FOODSTORE', 'RESTAURANT'

    Due to a lack of data, multiple values had to be assumed:

    - 'INDUSTRY': assumed to be equal to the value for the use type 'OFFICE'
    - 'HOSPITAL': assumed to be equal to the value for the use type 'HOTEL'
    - 'GYM', 'SWIMMING': assumed to be equal to the value for use type 'RETAIL'
    - 'SERVERROOM', 'COOLROOM': assumed negligible

    The following file is created as a side effect by this script:

    - total_LCA_mobility (.csv)
      csv file of yearly non-renewable primary energy demand and emissions due to mobility for each building

    Additional references:

    - [SIA_Effizienzpfad_2011]_

    :param locator: an InputLocator instance set to the scenario to work on
    :type locator: InputLocator

    .. [SIA_2040_2011] Swiss Society of Engineers and Architects (SIA). 2011. "SIA Efficiency Path 2040."
    .. [BFE_2012] Bundesamt fur Energie (BFE). 2012. "Arealentwicklung fur die 2000-Watt Gesellschaft:
        Beurteilungsmethode in Anlehnung an den SIA-Effizienzpfad Energie."
    .. [SIA_Effizienzpfad_2011] Swiss Society of Engineers and Architects (SIA). 2011. "SIA Effizienzpfad: Bestimmung
        der Ziel- und Richtwerte mit dem Top-Down Approach."
    .. [SIA_2024_2015]: Swiss Society of Engineers and Architects (SIA). 2015. "Merkblatt 2024: Raumnutzungsdaten fur
        die Energie- und Gebaeudetechnik."
    """

    # local files
    demand = pd.read_csv(locator.get_total_demand())
    prop_occupancy = gpdf.from_file(locator.get_building_occupancy()).drop('geometry', axis=1)#.set_index('Name')
    factors_mobility = pd.read_excel(locator.get_data_benchmark(), sheet_name='MOBILITY').drop('Description', axis=1).set_index("code")

    # calculate total_LCA_mobility: .csv
    occupancy_type = prop_occupancy.drop('Name', axis=1).columns.values
    non_renewable_energy = factors_mobility['NRE_today']
    emissions = factors_mobility['CO2_today']

    mobility = prop_occupancy.merge(demand,on='Name')
    fields_to_plot = ['Name', 'GFA_m2', 'M_nre_pen_GJ', 'M_nre_pen_MJm2', 'M_ghg_ton', 'M_ghg_kgm2']
    mobility[fields_to_plot[3]] = 0
    mobility[fields_to_plot[5]] = 0
    for occupancy in occupancy_type:
        if occupancy != "PARKING": # do not consider parking
            mobility[fields_to_plot[3]] += mobility[occupancy] * non_renewable_energy[occupancy]
            mobility[fields_to_plot[5]] += mobility[occupancy] * emissions[occupancy]
    mobility[fields_to_plot[2]] = mobility['GFA_m2'] * mobility[fields_to_plot[3]] / 1000
    mobility[fields_to_plot[4]] = mobility['GFA_m2'] * mobility[fields_to_plot[5]] / 1000

    mobility[fields_to_plot].to_csv(locator.get_lca_mobility(), index=False, float_format='%.2f')

def main(config):
    assert os.path.exists(config.scenario), 'Scenario not found: %s' % config.scenario
    locator = cea.inputlocator.InputLocator(scenario=config.scenario)

    print("Running mobility with scenario = %s" % config.scenario)

    lca_mobility(locator=locator, config=config)

if __name__ == '__main__':
    main(cea.config.Configuration())

<commit_msg>REFERENCE is not an occupancy type...<commit_after>"""
Primary energy and CO2 emissions model algorithm for mobility.
"""

from __future__ import division

import os
import pandas as pd
from geopandas import GeoDataFrame as gpdf
import cea.inputlocator

__author__ = "Martin Mosteiro Romero"
__copyright__ = "Copyright 2016, Architecture and Building Systems - ETH Zurich"
__credits__ = ["Martin Mosteiro Romero"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daren Thomas"
__email__ = "cea@arch.ethz.ch"
__status__ = "Production"

def lca_mobility(locator, config):
    """
    Calculation of the primary energy and CO2 emissions for mobility in the area based on the present day values
    calculated for the 2000 Watt society target.

    The current values for the Swiss case for each type of occupancy are based on the following sources:

    - [SIA_2040_2011]_: 'MULTI_RES', 'SINGLE_RES', 'SCHOOL', 'OFFICE'
    - [BFE_2012]_: 'HOTEL', 'RETAIL', 'FOODSTORE', 'RESTAURANT'

    Due to a lack of data, multiple values had to be assumed:

    - 'INDUSTRY': assumed to be equal to the value for the use type 'OFFICE'
    - 'HOSPITAL': assumed to be equal to the value for the use type 'HOTEL'
    - 'GYM', 'SWIMMING': assumed to be equal to the value for use type 'RETAIL'
    - 'SERVERROOM', 'COOLROOM': assumed negligible

    The following file is created as a side effect by this script:

    - total_LCA_mobility (.csv)
      csv file of yearly non-renewable primary energy demand and emissions due to mobility for each building

    Additional references:

    - [SIA_Effizienzpfad_2011]_

    :param locator: an InputLocator instance set to the scenario to work on
    :type locator: InputLocator

    .. [SIA_2040_2011] Swiss Society of Engineers and Architects (SIA). 2011. "SIA Efficiency Path 2040."
    .. [BFE_2012] Bundesamt fur Energie (BFE). 2012. "Arealentwicklung fur die 2000-Watt Gesellschaft:
        Beurteilungsmethode in Anlehnung an den SIA-Effizienzpfad Energie."
    .. [SIA_Effizienzpfad_2011] Swiss Society of Engineers and Architects (SIA). 2011. "SIA Effizienzpfad: Bestimmung
        der Ziel- und Richtwerte mit dem Top-Down Approach."
    .. [SIA_2024_2015]: Swiss Society of Engineers and Architects (SIA). 2015. "Merkblatt 2024: Raumnutzungsdaten fur
        die Energie- und Gebaeudetechnik."
    """

    # local files
    demand = pd.read_csv(locator.get_total_demand())
    prop_occupancy = gpdf.from_file(locator.get_building_occupancy()).drop('geometry', axis=1)#.set_index('Name')
    factors_mobility = pd.read_excel(locator.get_data_benchmark(), sheet_name='MOBILITY').drop('Description', axis=1).set_index("code")

    # calculate total_LCA_mobility: .csv
    occupancy_type = filter(lambda o: o != "REFERENCE", prop_occupancy.drop('Name', axis=1).columns.values)
    non_renewable_energy = factors_mobility['NRE_today']
    emissions = factors_mobility['CO2_today']

    mobility = prop_occupancy.merge(demand,on='Name')
    fields_to_plot = ['Name', 'GFA_m2', 'M_nre_pen_GJ', 'M_nre_pen_MJm2', 'M_ghg_ton', 'M_ghg_kgm2']
    mobility[fields_to_plot[3]] = 0
    mobility[fields_to_plot[5]] = 0
    for occupancy in occupancy_type:
        if occupancy != "PARKING": # do not consider parking
            mobility[fields_to_plot[3]] += mobility[occupancy] * non_renewable_energy[occupancy]
            mobility[fields_to_plot[5]] += mobility[occupancy] * emissions[occupancy]
    mobility[fields_to_plot[2]] = mobility['GFA_m2'] * mobility[fields_to_plot[3]] / 1000
    mobility[fields_to_plot[4]] = mobility['GFA_m2'] * mobility[fields_to_plot[5]] / 1000

    mobility[fields_to_plot].to_csv(locator.get_lca_mobility(), index=False, float_format='%.2f')

def main(config):
    assert os.path.exists(config.scenario), 'Scenario not found: %s' % config.scenario
    locator = cea.inputlocator.InputLocator(scenario=config.scenario)

    print("Running mobility with scenario = %s" % config.scenario)

    lca_mobility(locator=locator, config=config)

if __name__ == '__main__':
    main(cea.config.Configuration())

<|endoftext|>


<commit_before>###############################################################################
#
# Copyright (c) 2012 Ruslan Spivak
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
###############################################################################

__author__ = 'Ruslan Spivak <ruslan.spivak@gmail.com>'

import os
import fnmatch
import hashlib
import sys
import optparse

try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

import gzip
from collections import defaultdict

import yaml
import slimit
import cssmin

OUTPUT_DIR = 'assets'
CONFIG_FILE = 'assets.yaml'
ASSETS_INFO_FILE = 'assetsinfo.yaml'

def _log(msg):
    sys.stderr.write('%s\n' % msg)

def load_config(path):
    return yaml.load(open(path))

class AssetManager(object):
    """I manage assets bundles."""
    def __init__(self, config, basedir=None):
        self.config = config
        self.basedir = basedir or os.getcwd()

    def _get_bundles_by_type(self, type):
        """Get a dictionary of bundles for requested type.

        Args:
            type: 'javascript' or 'css'
        """
        bundles = {}
        bundle_definitions = self.config.get(type)
        if bundle_definitions is None:
            return bundles
        # bundle name: common
        for bundle_name, paths in bundle_definitions.items():
            bundle_files = []
            # path: static/js/vendor/*.js
            for path in paths:
                # pattern: /tmp/static/js/vendor/*.js
                pattern = abspath = os.path.join(self.basedir, path)
                # assetdir: /tmp/static/js/vendor
                # assetdir contents:
                #  - /tmp/static/js/vendor/t1.js
                #  - /tmp/static/js/vendor/t2.js
                #  - /tmp/static/js/vendor/index.html
                assetdir = os.path.dirname(abspath)
                # expanded_fnames after filtering using the pattern:
                #  - /tmp/static/js/vendor/t1.js
                #  - /tmp/static/js/vendor/t2.js
                fnames = [os.path.join(assetdir, fname)
                          for fname in os.listdir(assetdir)]
                expanded_fnames = fnmatch.filter(fnames, pattern)
                bundle_files.extend(expanded_fnames)
            bundles[bundle_name] = bundle_files

        return bundles

    def _compress(self, data):
        compresslevel = 9 # max
        buffer = StringIO.StringIO()
        with gzip.GzipFile(fileobj=buffer, mode='wb',
                           compresslevel=compresslevel) as fout:
            fout.write(data)
        return buffer.getvalue()

    def _process_bundle(self, name, paths, type):
        sha1, sep = '', ''
        raw_data = ''.join(open(path).read() for path in paths)
        if self.config.get('fingerprint'):
            sha1, sep = hashlib.sha1(raw_data).hexdigest(), '-'
        file_ext = {
            'javascript': '.js',
            'css': '.css',
            }.get(type)
        fname_template = '%s%s%s{suffix}%s{gz}' % (name, sep, sha1, file_ext)
        raw_fname = fname_template.format(suffix='', gz='')
        self.write(raw_fname, raw_data)

        if type == 'javascript':
            minified_data = slimit.minify(raw_data, mangle=True)
        elif type == 'css':
            minified_data = cssmin.cssmin(raw_data)
        minifed_fname = fname_template.format(suffix='.min', gz='')
        self.write(minifed_fname, minified_data)

        gzipped_data = self._compress(minified_data)
        gzipped_fname = fname_template.format(suffix='.min', gz='.gz')
        self.write(gzipped_fname, gzipped_data)

        bundle_info = {
            name: {
                'fingerprint': sha1,
                'output': {
                    'raw': raw_fname,
                    'min': minifed_fname,
                    'gz': gzipped_fname,
                    },
                'size': {
                    'raw': len(raw_data),
                    'min': len(minified_data),
                    'gz': len(gzipped_data),
                    },
                }
            }
        return bundle_info

    def write(self, fname, data):
        output = os.path.abspath(self.config.get('output', OUTPUT_DIR))
        if not os.path.exists(output):
            os.makedirs(output)
        path = os.path.join(output, fname)
        with open(path, 'w') as fout:
            fout.write(data)

    def write_info(self, bundles_info):
        self.write(ASSETS_INFO_FILE,
                   yaml.dump(dict(bundles_info), default_flow_style=False))

    def get_bundles(self):
        bundles = {
            'javascript': self._get_bundles_by_type('javascript'),
            'css': self._get_bundles_by_type('css'),
            }
        return bundles

    def process_bundles(self):
        info = defaultdict(dict)
        bundles = self.get_bundles()
        for bundle_type in bundles:
            for name, paths in bundles[bundle_type].items():
                bundle_info = self._process_bundle(name, paths, bundle_type)
                info[bundle_type].update(bundle_info)
        return info

def main():
    parser = optparse.OptionParser()
    parser.add_option('-c', '--config', dest='config',
                      help='path to assets.yaml (default: ./assets.yaml)')
    parser.add_option('-b', '--basedir', dest='basedir',
                      help=('base directory to which all '
                            'assets paths are relative (default: ./)'))
    options, args = parser.parse_args()

    config_path = options.config
    if config_path is None:
        config_path = os.path.join(os.getcwd(), CONFIG_FILE)

    if not os.path.exists(config_path):
        _log('Could not find the asset configuration file "%s"' % config_path)
        sys.exit(1)

    config = load_config(config_path)
    manager = AssetManager(config, options.basedir)
    bundles_info = manager.process_bundles()
    manager.write_info(bundles_info)
<commit_msg>Move around<commit_after>###############################################################################
#
# Copyright (c) 2012 Ruslan Spivak
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
###############################################################################

__author__ = 'Ruslan Spivak <ruslan.spivak@gmail.com>'

import os
import fnmatch
import hashlib
import sys
import optparse
import gzip
from collections import defaultdict
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

import yaml
import slimit
import cssmin

OUTPUT_DIR = 'assets'
CONFIG_FILE = 'assets.yaml'
ASSETS_INFO_FILE = 'assetsinfo.yaml'

def _log(msg):
    sys.stderr.write('%s\n' % msg)

def load_config(path):
    return yaml.load(open(path))

class AssetManager(object):
    """I manage assets bundles."""
    def __init__(self, config, basedir=None):
        self.config = config
        self.basedir = basedir or os.getcwd()

    def _get_bundles_by_type(self, type):
        """Get a dictionary of bundles for requested type.

        Args:
            type: 'javascript' or 'css'
        """
        bundles = {}
        bundle_definitions = self.config.get(type)
        if bundle_definitions is None:
            return bundles
        # bundle name: common
        for bundle_name, paths in bundle_definitions.items():
            bundle_files = []
            # path: static/js/vendor/*.js
            for path in paths:
                # pattern: /tmp/static/js/vendor/*.js
                pattern = abspath = os.path.join(self.basedir, path)
                # assetdir: /tmp/static/js/vendor
                # assetdir contents:
                #  - /tmp/static/js/vendor/t1.js
                #  - /tmp/static/js/vendor/t2.js
                #  - /tmp/static/js/vendor/index.html
                assetdir = os.path.dirname(abspath)
                # expanded_fnames after filtering using the pattern:
                #  - /tmp/static/js/vendor/t1.js
                #  - /tmp/static/js/vendor/t2.js
                fnames = [os.path.join(assetdir, fname)
                          for fname in os.listdir(assetdir)]
                expanded_fnames = fnmatch.filter(fnames, pattern)
                bundle_files.extend(expanded_fnames)
            bundles[bundle_name] = bundle_files

        return bundles

    def _compress(self, data):
        compresslevel = 9 # max
        buffer = StringIO.StringIO()
        with gzip.GzipFile(fileobj=buffer, mode='wb',
                           compresslevel=compresslevel) as fout:
            fout.write(data)
        return buffer.getvalue()

    def _process_bundle(self, name, paths, type):
        sha1, sep = '', ''
        raw_data = ''.join(open(path).read() for path in paths)
        if self.config.get('fingerprint'):
            sha1, sep = hashlib.sha1(raw_data).hexdigest(), '-'
        file_ext = {
            'javascript': '.js',
            'css': '.css',
            }.get(type)
        fname_template = '%s%s%s{suffix}%s{gz}' % (name, sep, sha1, file_ext)
        raw_fname = fname_template.format(suffix='', gz='')
        self.write(raw_fname, raw_data)

        if type == 'javascript':
            minified_data = slimit.minify(raw_data, mangle=True)
        elif type == 'css':
            minified_data = cssmin.cssmin(raw_data)
        minifed_fname = fname_template.format(suffix='.min', gz='')
        self.write(minifed_fname, minified_data)

        gzipped_data = self._compress(minified_data)
        gzipped_fname = fname_template.format(suffix='.min', gz='.gz')
        self.write(gzipped_fname, gzipped_data)

        bundle_info = {
            name: {
                'fingerprint': sha1,
                'output': {
                    'raw': raw_fname,
                    'min': minifed_fname,
                    'gz': gzipped_fname,
                    },
                'size': {
                    'raw': len(raw_data),
                    'min': len(minified_data),
                    'gz': len(gzipped_data),
                    },
                }
            }
        return bundle_info

    def write(self, fname, data):
        output = os.path.abspath(self.config.get('output', OUTPUT_DIR))
        if not os.path.exists(output):
            os.makedirs(output)
        path = os.path.join(output, fname)
        with open(path, 'w') as fout:
            fout.write(data)

    def write_info(self, bundles_info):
        self.write(ASSETS_INFO_FILE,
                   yaml.dump(dict(bundles_info), default_flow_style=False))

    def get_bundles(self):
        bundles = {
            'javascript': self._get_bundles_by_type('javascript'),
            'css': self._get_bundles_by_type('css'),
            }
        return bundles

    def process_bundles(self):
        info = defaultdict(dict)
        bundles = self.get_bundles()
        for bundle_type in bundles:
            for name, paths in bundles[bundle_type].items():
                bundle_info = self._process_bundle(name, paths, bundle_type)
                info[bundle_type].update(bundle_info)
        return info

def main():
    parser = optparse.OptionParser()
    parser.add_option('-c', '--config', dest='config',
                      help='path to assets.yaml (default: ./assets.yaml)')
    parser.add_option('-b', '--basedir', dest='basedir',
                      help=('base directory to which all '
                            'assets paths are relative (default: ./)'))
    options, args = parser.parse_args()

    config_path = options.config
    if config_path is None:
        config_path = os.path.join(os.getcwd(), CONFIG_FILE)

    if not os.path.exists(config_path):
        _log('Could not find the asset configuration file "%s"' % config_path)
        sys.exit(1)

    config = load_config(config_path)
    manager = AssetManager(config, options.basedir)
    bundles_info = manager.process_bundles()
    manager.write_info(bundles_info)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""

Author
------
Bo Zhang

Email
-----
bozhang@nao.cas.cn

Created on
----------
- Wed Feb  24 15:05:00 2016     spec_quick_view

Modifications
-------------
-

Aims
----
- method spec_quick_view: tool for quick view of spectra

"""

import numpy as np
import matplotlib.pyplot as plt
import astropy.constants as const
from astropy.table import Table
from bopy.spec.spec import spec_quick_init, Spec, norm_spec_pixel, wave2ranges

# input: a list of spectra
# todo : norm spectra --> plot spectra --> print file

def _spec_list_to_spec_chunk_list(n_spec, n_chunks, spec_list,
                                  norm_type=None, wave_intervals=None,
                                  flux_amp=None,
                                  q=0.90, delta_lambda=100., verbose=False):

    # -----------------------------------------------------------------
    # norm_type:
    #   - None:             'flux' column directly used
    #   - 'continuum':      continuum normalization
    #   - 'median':         normalized to the median of whole spectrum
    #   - 'chunk_median:    normalized to the median of each chunk
    #   - 'pixel**':        normalized to a given pixel
    #                        useful for stellar population spectra
    # -----------------------------------------------------------------

    if norm_type is None or norm_type == 'chunk_median':
        # directly use the 'flux' column
        # or median normalization after breaking into chunks
        pass

    elif norm_type == 'continuum':  # PROBLEM!!!
        # 'continuum'
        for i in xrange(n_spec):
            if verbose:
                print('@Cham: spec_list >> spec_chunk_list [continuum][%s]'%i)
            spec_list[i].norm_spec_running_q(
                ranges=None,
                q=q, delta_lambda=delta_lambda,
                overwrite_flux=True, verbose=verbose)

    elif norm_type == 'median':
        # 'median'
        for i in xrange(n_spec):
            spec_list[i].norm_spec_median()

    elif norm_type[0:5] == 'pixel':
        # 'pixel'
        norm_wave = np.float64(norm_type[5:])
        for i in xrange(n_spec):
            spec_list[i].norm_spec_pixel(norm_wave)

    else:
        raise ValueError('norm_type wrong!')

    spec_chunk_list = [spec.extract_chunk_wave_interval(wave_intervals)
                       for spec in spec_list]

    if norm_type == 'chunk_median':
        # norm each spec chunk to its median
        for i in xrange(n_spec):
            for j in xrange(n_chunks):
                spec_chunk_list[i][j].norm_spec_median()

    # amplify spec chunks if necessary
    if flux_amp is not None:
        flux_amp = np.array(flux_amp)
        assert len(flux_amp) == n_chunks
        for i in xrange(n_spec):
            for j in xrange(n_chunks):
                spec_chunk_list[i][j]['flux'] = \
                    (spec_chunk_list[i][j]['flux'] - 1.) * flux_amp[j] + 1.

    return spec_chunk_list

def _calculate_wave_offset(n_spec, n_chunks, wave_intervals=None,
                           xtick_gap_fraction=0.05, plot_start=0.):
    assert wave_intervals is not None

    # total span of spec chunks
    wave_totalspan = np.sum(np.diff(wave_intervals))
    # gap is a fraction of total span
    if n_chunks > 1:
        wave_gap = xtick_gap_fraction * wave_totalspan / (n_chunks - 1)
    else:
        wave_gap = 0

    # calculate wave_offset, i.e.,
    # how much wavelength should shift (to the left side)
    wave_offset_intercept = wave_intervals[0, 0] - plot_start
    wave_offset_spec = np.hstack((np.array(0), (wave_intervals[1:,0]-wave_intervals[:-1,1]).flatten()))
    wave_offset_gap = np.roll(np.ones(n_chunks) * wave_gap, 1)
    wave_offset_gap[0] = 0
    wave_offset = np.cumsum(wave_offset_spec - wave_offset_gap) + wave_offset_intercept
    return wave_offset

def _xtick_pos_lab(n_chunks,
                   wave_centers, wave_intervals, wave_offset,
                   xtick_step,
                   xtick_format_str=('%.0f', '%.1f'),
                   xtick_label_type='wavelength'):
    # reshape wave_centers if necessary
    wave_centers = np.array(wave_centers)
    if wave_centers.ndim == 1:
        wave_centers = wave_centers.reshape((wave_centers.shape[0], 1))

    # initialize results
    xtick_pos, xtick_lab = [], []

    # xtick_label_type
    if xtick_label_type == 'velocity':
        # need to trasform 'wave_intervals' array to pseudo-volocity array
        # array
        xtick_pos_step = xtick_step * wave_centers / (const.c.value / 1000.)
        # float
        xtick_lab_step = xtick_step
    elif xtick_label_type == 'wavelength':
        # array
        xtick_pos_step = np.ones_like(wave_centers) * xtick_step
        # float
        xtick_lab_step = xtick_step
    else:
        raise ValueError('@Cham: xtick_label_type is wrong!')

    for i_chunk in xrange(n_chunks):
        n_xtick_l = np.int(np.abs((wave_centers[i_chunk] - wave_intervals[i_chunk][0]) / xtick_pos_step[i_chunk]))
        n_xtick_r = np.int(np.abs((wave_centers[i_chunk] - wave_intervals[i_chunk][1]) / xtick_pos_step[i_chunk]))
        xtick_pos_, xtick_lab_ = _generate_chunk_xtick_pos_lab(
            n_xtick_l,
            n_xtick_r,
            wave_centers[i_chunk],
            xtick_pos_step=xtick_pos_step[i_chunk],
            xtick_lab_step=xtick_lab_step,
            wave_offset_chunk=wave_offset[i_chunk],
            xtick_format_str=xtick_format_str)
        xtick_pos.extend(xtick_pos_)
        xtick_lab.extend(xtick_lab_)
    return xtick_pos, xtick_lab

def _generate_chunk_xtick_pos_lab(n_xtick_l,
                                  n_xtick_r,
                                  wave_center,
                                  xtick_pos_step=50.,
                                  xtick_lab_step=50.,
                                  wave_offset_chunk=0.,
                                  xtick_format_str=('%.0f', '%.1f')):
    # relative xtick position
    xtick_pos_rel = np.arange(-n_xtick_l, n_xtick_r+1) * xtick_pos_step
    xtick_pos = xtick_pos_rel + wave_center - wave_offset_chunk

    # xtick labels
    xtick_lab_rel = np.arange(-n_xtick_l, n_xtick_r+1) * xtick_lab_step
    xtick_lab = [xtick_format_str[0] % xtick_lab_rel_ for xtick_lab_rel_ in xtick_lab_rel]
    xtick_lab[n_xtick_l] = xtick_format_str[1] % wave_center

    return xtick_pos, xtick_lab

def spec_quick_view(ax,
                    spec_list,
                    norm_type='median',
                    q=0.90,
                    delta_lambda=100.,
                    wave_intervals=None,
                    wave_centers = None,
                    flux_amp=None,
                    xtick_modify=True,
                    xtick_step = 50.,
                    xtick_gap_fraction = .1,
                    xtick_start=0.,
                    xtick_format_str=('%.0f', '%.1f'),
                    xtick_label_type='wavelength',
                    offset_perspec=0.5,
                    verbose=False,
                    *args, **kwargs):
    """
    Parameters
    ----------
    ax: axes
        the axes on which spectra will be plotted
    spec_list: list of list
        a list of spec
    norm_type: string
        type of normalization
    q: float
        smoothness
    delta_lambda: float
        bin width
    wave_intervals: Nx2 array
        wavelength intervals of spec chunks
    wave_centers: Nx1 array
        wavelength centers of spec chunk labels
    flux_amp: None of Nx1 array
        amplifiers of each chunk
    xtick_modify: bool
        if True, modify the xticks
    xtick_step: float
        the step of xticks
    xtick_gap_fraction: float
        the fraction of the gap to the total span
    xtick_start: float
        the real start X, default is 0
    xtick_format_str: tuple of two strings
        the format strings of xtick labels
    xtick_label_type: string
        'wavelength' or 'velocity'
    offset_perspec: float
        offset between specs
    verbose: bool
        if True, print details
    *args, **kwargs:
        other plot (keyword) arguments

    Returns
    -------
    ax, spec_chunk_list, wave_offset, offset_specs, xlim

    """
    # 0. calculate some basic parameters
    if wave_intervals is None:
        n_chunks = 1
        xtick_modify = False
    else:
        wave_intervals = np.array(wave_intervals)
        n_chunks = len(wave_intervals)
    n_spec = len(spec_list)
    if verbose:
        print('@Cham: wave_interval:')
        print(wave_intervals)
        print('@Cham: n_spec = %s, n_chunks = %s' % (n_spec, n_chunks))

    # 1. spec_list --> spec_chunk_list
    # if verbose:
    #     print('@Cham: spec_list >> spec_chunk_list [%s]' % norm_type)
    spec_chunk_list = _spec_list_to_spec_chunk_list(
        n_spec, n_chunks, spec_list, norm_type, wave_intervals,
        flux_amp=flux_amp, q=q, delta_lambda=delta_lambda, verbose=verbose)  #

    # norm_type:
    #   - None:             'flux' column directly used
    #   - 'continuum':      continuum normalization
    #   - 'median':         normalized to the median of whole spectrum
    #   - 'chunk_median:    normalized to the median of each chunk
    #   - 'pixel**':        normalized to a given pixel
    #                        useful for stellar population spectra

    # 2. calculate offset in X and Y directions

    # offset in Y direction (between specs)
    offset_specs = np.arange(n_spec) * offset_perspec

    # offset in X direction (between spec chunks)
    if not xtick_modify:
        # just plot spec chunks, don't modify XTICK & XTICKLABELS
        for i in xrange(n_spec):
            # for each list of spec chunks, do these:
            spec_chunks = spec_chunk_list[i]
            for j in xrange(n_chunks):
                spec_chunk = spec_chunks[j]
                ax.plot(spec_chunk['wave'],
                        spec_chunk['flux'] + offset_specs[i],
                        *args, **kwargs)
        # set xlim
        # xlim = np.min(wave_intervals[:, 0]), np.max(wave_intervals[:, 1])
        # ax.set_xlim(xlim)
        wave_offset, xlim = None, None

    else:
        assert wave_intervals is not None
        # need to modify XTICK & XTICKLABELS

        # 1> calculate wave_offset
        wave_offset = _calculate_wave_offset(n_spec, n_chunks, wave_intervals,
                                             xtick_gap_fraction, xtick_start)
        # print 'wave_offset', wave_offset
        for i in xrange(n_spec):
            # for each list of spec chunks, do these:
            spec_chunks = spec_chunk_list[i]
            for j in xrange(n_chunks):
                spec_chunk = spec_chunks[j]
                ax.plot(spec_chunk['wave'] - wave_offset[j],  # wave_offset
                        spec_chunk['flux'] + offset_specs[i],
                        *args, **kwargs)

        # 2> calculate xtick position & labels
        xtick_pos, xtick_lab = _xtick_pos_lab(
            n_chunks, wave_centers, wave_intervals, wave_offset, xtick_step,
            xtick_format_str=xtick_format_str,
            xtick_label_type=xtick_label_type)

        # set xtick
        ax.set_xticks(xtick_pos)
        # set xticklabels
        ax.set_xticklabels(xtick_lab)
        # set xlim
        xlim = np.min(wave_intervals[:, 0].flatten()-wave_offset.flatten()),\
               np.max(wave_intervals[:, 1].flatten()-wave_offset.flatten())
        ax.set_xlim(xlim)

    return ax, spec_chunk_list, wave_offset, offset_specs, xlim

def spec_quick_view_fiducial(ax, offset_specs, x=None, *args, **kwargs):
    """
    Parameters
    ----------
    ax: axes
        the axes on which fiducial lines will be plot
    offset_specs: array
        the Y offset values
    x: two-element array
        default is ax.get_xlim()

    *args, **kwargs:
        other plot parameters

    """
    fiducial_levels = np.array(offset_specs).flatten() + 1
    fiducial_levels = fiducial_levels.reshape(1, -1)
    fiducial_levels = fiducial_levels.repeat(2, axis=0)
    if x is None:
        x = np.array(ax.get_xlim()).flatten().reshape(-1, 1)

    ax.plot(x, fiducial_levels, *args, **kwargs)

    return None

def test_spec_quick_view():
    # read BC03 catalog
    print('-------------------------------------------------------')
    print('@Cham: reading BC03 catalog ...')
    bc03cat = Table.read(
        '/home/cham/PycharmProjects/bopy/bopy/data/model_bc03/Base.BC03.N.csv')
    bc03dir = '/home/cham/PycharmProjects/bopy/bopy/data/model_bc03/'

    # prepare wave_intervals
    wave_intervals = [[5775, 5785], [6280., 6290]]

    # break spectra into chunks
    print('@Cham: loading BC03 spectra ...')
    spec_list = []
    for i in xrange(10):
        fp = bc03dir + bc03cat['specfile'][i]
        spec = np.loadtxt(fp)
        spec = Spec(Table(spec, names=['wave', 'flux']))
        # spec = norm_spec_pixel(spec, 6000.)
        # spec_chunks_list.append(
        #     spec.extract_chunk_wave_interval(wave_intervals))
        spec_list.append(spec)

    # plot spec chunks
    print('@Cham: ploting spec chunks using spec_quick_view() ...')
    fig = plt.figure('test spec_quick_view method', figsize=(10, 20))
    ax = fig.add_subplot(111)
    ax, spec_chunk_list, wave_offset, offset_specs, xlim = \
        spec_quick_view(ax,
                        spec_list,
                        norm_type='continuum',
                        q=0.90,
                        delta_lambda=100.,
                        wave_intervals=wave_intervals,
                        wave_centers=[5780.1, 6284.1],
                        flux_amp=None,
                        xtick_modify=True,
                        xtick_step=50.,
                        xtick_gap_fraction=0.02,
                        xtick_format_str=('%.0f', '%.1f'),
                        xtick_label_type='velocity',
                        offset_perspec=0.05,
                        verbose=True,
                        ls='--',
                        c='r')

    spec_quick_view_fiducial(ax, offset_specs, None, 'k--')

    # print spec_chunk_list[0][0]
    print('@Cham: saving figure ...')
    fig.savefig('/home/cham/PycharmProjects/bopy/bopy/data/test_spec_quick_view/test.pdf')
    print('@Cham: test spec_quick_view OK!')

    return 0

if __name__ == '__main__':
    test_spec_quick_view()

<commit_msg>update spec_quick_view.py<commit_after># -*- coding: utf-8 -*-
"""

Author
------
Bo Zhang

Email
-----
bozhang@nao.cas.cn

Created on
----------
- Wed Feb  24 15:05:00 2016     spec_quick_view

Modifications
-------------
-

Aims
----
- method spec_quick_view: tool for quick view of spectra

"""

import numpy as np
import matplotlib.pyplot as plt
import astropy.constants as const
from astropy.table import Table
from bopy.spec.spec import spec_quick_init, Spec, norm_spec_pixel, wave2ranges

# input: a list of spectra
# todo : norm spectra --> plot spectra --> print file

def _spec_list_to_spec_chunk_list(n_spec, n_chunks, spec_list,
                                  norm_type=None, wave_intervals=None,
                                  flux_amp=None,
                                  q=0.90, delta_lambda=100., verbose=False):

    # -----------------------------------------------------------------
    # norm_type:
    #   - None:             'flux' column directly used
    #   - 'continuum':      continuum normalization
    #   - 'median':         normalized to the median of whole spectrum
    #   - 'chunk_median:    normalized to the median of each chunk
    #   - 'pixel**':        normalized to a given pixel
    #                        useful for stellar population spectra
    # -----------------------------------------------------------------

    if norm_type is None or norm_type == 'chunk_median':
        # directly use the 'flux' column
        # or median normalization after breaking into chunks
        pass

    elif norm_type == 'continuum':  # PROBLEM!!!
        # 'continuum'
        for i in xrange(n_spec):
            if verbose:
                print('@Cham: spec_list >> spec_chunk_list [continuum][%s]'%i)
            spec_list[i].norm_spec_running_q(
                ranges=None,
                q=q, delta_lambda=delta_lambda,
                overwrite_flux=True, verbose=verbose)

    elif norm_type == 'median':
        # 'median'
        for i in xrange(n_spec):
            spec_list[i].norm_spec_median()

    elif norm_type[0:5] == 'pixel':
        # 'pixel'
        norm_wave = np.float64(norm_type[5:])
        for i in xrange(n_spec):
            spec_list[i].norm_spec_pixel(norm_wave)

    else:
        raise ValueError('norm_type wrong!')

    spec_chunk_list = [spec.extract_chunk_wave_interval(wave_intervals)
                       for spec in spec_list]

    if norm_type == 'chunk_median':
        # norm each spec chunk to its median
        for i in xrange(n_spec):
            for j in xrange(n_chunks):
                spec_chunk_list[i][j].norm_spec_median()

    # amplify spec chunks if necessary
    if flux_amp is not None:
        flux_amp = np.array(flux_amp)
        assert len(flux_amp) == n_chunks
        for i in xrange(n_spec):
            for j in xrange(n_chunks):
                spec_chunk_list[i][j]['flux'] = \
                    (spec_chunk_list[i][j]['flux'] - 1.) * flux_amp[j] + 1.

    return spec_chunk_list

def _calculate_wave_offset(n_spec, n_chunks, wave_intervals=None,
                           xtick_gap_fraction=0.05, plot_start=0.):
    assert wave_intervals is not None

    # total span of spec chunks
    wave_totalspan = np.sum(np.diff(wave_intervals))
    # gap is a fraction of total span
    if n_chunks > 1:
        wave_gap = xtick_gap_fraction * wave_totalspan / (n_chunks - 1)
    else:
        wave_gap = 0

    # calculate wave_offset, i.e.,
    # how much wavelength should shift (to the left side)
    wave_offset_intercept = wave_intervals[0, 0] - plot_start
    wave_offset_spec = np.hstack((np.array(0), (wave_intervals[1:,0]-wave_intervals[:-1,1]).flatten()))
    wave_offset_gap = np.roll(np.ones(n_chunks) * wave_gap, 1)
    wave_offset_gap[0] = 0
    wave_offset = np.cumsum(wave_offset_spec - wave_offset_gap) + wave_offset_intercept
    return wave_offset

def _xtick_pos_lab(n_chunks,
                   wave_centers, wave_intervals, wave_offset,
                   xtick_step,
                   xtick_format_str=('%.0f', '%.1f'),
                   xtick_label_type='wavelength'):
    # reshape wave_centers if necessary
    wave_centers = np.array(wave_centers)
    if wave_centers.ndim == 1:
        wave_centers = wave_centers.reshape((wave_centers.shape[0], 1))

    # initialize results
    xtick_pos, xtick_lab = [], []

    # xtick_label_type
    if xtick_label_type == 'velocity':
        # need to trasform 'wave_intervals' array to pseudo-volocity array
        # array
        xtick_pos_step = xtick_step * wave_centers / (const.c.value / 1000.)
        # float
        xtick_lab_step = xtick_step
    elif xtick_label_type == 'wavelength':
        # array
        xtick_pos_step = np.ones_like(wave_centers) * xtick_step
        # float
        xtick_lab_step = xtick_step
    else:
        raise ValueError('@Cham: xtick_label_type is wrong!')

    for i_chunk in xrange(n_chunks):
        n_xtick_l = np.int(np.abs((wave_centers[i_chunk] - wave_intervals[i_chunk][0]) / xtick_pos_step[i_chunk]))
        n_xtick_r = np.int(np.abs((wave_centers[i_chunk] - wave_intervals[i_chunk][1]) / xtick_pos_step[i_chunk]))
        xtick_pos_, xtick_lab_ = _generate_chunk_xtick_pos_lab(
            n_xtick_l,
            n_xtick_r,
            wave_centers[i_chunk],
            xtick_pos_step=xtick_pos_step[i_chunk],
            xtick_lab_step=xtick_lab_step,
            wave_offset_chunk=wave_offset[i_chunk],
            xtick_format_str=xtick_format_str)
        xtick_pos.extend(xtick_pos_)
        xtick_lab.extend(xtick_lab_)
    return xtick_pos, xtick_lab

def _generate_chunk_xtick_pos_lab(n_xtick_l,
                                  n_xtick_r,
                                  wave_center,
                                  xtick_pos_step=50.,
                                  xtick_lab_step=50.,
                                  wave_offset_chunk=0.,
                                  xtick_format_str=('%.0f', '%.1f')):
    # relative xtick position
    xtick_pos_rel = np.arange(-n_xtick_l, n_xtick_r+1) * xtick_pos_step
    xtick_pos = xtick_pos_rel + wave_center - wave_offset_chunk

    # xtick labels
    xtick_lab_rel = np.arange(-n_xtick_l, n_xtick_r+1) * xtick_lab_step
    xtick_lab = [xtick_format_str[0] % xtick_lab_rel_ for xtick_lab_rel_ in xtick_lab_rel]
    xtick_lab[n_xtick_l] = xtick_format_str[1] % wave_center

    return xtick_pos, xtick_lab

def spec_quick_view(ax,
                    spec_list,
                    norm_type='median',
                    q=0.90,
                    delta_lambda=100.,
                    wave_intervals=None,
                    wave_centers = None,
                    flux_amp=None,
                    xtick_modify=True,
                    xtick_step = 50.,
                    xtick_gap_fraction = .1,
                    xtick_start=0.,
                    xtick_format_str=('%.0f', '%.1f'),
                    xtick_label_type='wavelength',
                    offset_perspec=0.5,
                    verbose=False,
                    *args, **kwargs):
    """
    Parameters
    ----------
    ax: axes
        the axes on which spectra will be plotted
    spec_list: list of list
        a list of spec
    norm_type: string
        type of normalization
    q: float
        smoothness
    delta_lambda: float
        bin width
    wave_intervals: Nx2 array
        wavelength intervals of spec chunks
    wave_centers: Nx1 array
        wavelength centers of spec chunk labels
    flux_amp: None of Nx1 array
        amplifiers of each chunk
    xtick_modify: bool
        if True, modify the xticks
    xtick_step: float
        the step of xticks
    xtick_gap_fraction: float
        the fraction of the gap to the total span
    xtick_start: float
        the real start X, default is 0
    xtick_format_str: tuple of two strings
        the format strings of xtick labels
    xtick_label_type: string
        'wavelength' or 'velocity'
    offset_perspec: float
        offset between specs
    verbose: bool
        if True, print details
    *args, **kwargs:
        other plot (keyword) arguments

    Returns
    -------
    ax, spec_chunk_list, wave_offset, offset_specs, xlim

    """
    # 0. calculate some basic parameters
    if wave_intervals is None:
        n_chunks = 1
        xtick_modify = False
    else:
        wave_intervals = np.array(wave_intervals)
        n_chunks = len(wave_intervals)
    n_spec = len(spec_list)
    if verbose:
        print('@Cham: wave_interval:')
        print(wave_intervals)
        print('@Cham: n_spec = %s, n_chunks = %s' % (n_spec, n_chunks))

    # 1. spec_list --> spec_chunk_list
    # if verbose:
    #     print('@Cham: spec_list >> spec_chunk_list [%s]' % norm_type)
    spec_chunk_list = _spec_list_to_spec_chunk_list(
        n_spec, n_chunks, spec_list, norm_type, wave_intervals,
        flux_amp=flux_amp, q=q, delta_lambda=delta_lambda, verbose=verbose)  #

    # norm_type:
    #   - None:             'flux' column directly used
    #   - 'continuum':      continuum normalization
    #   - 'median':         normalized to the median of whole spectrum
    #   - 'chunk_median:    normalized to the median of each chunk
    #   - 'pixel**':        normalized to a given pixel
    #                        useful for stellar population spectra

    # 2. calculate offset in X and Y directions

    # offset in Y direction (between specs)
    offset_specs = np.arange(n_spec) * offset_perspec

    # offset in X direction (between spec chunks)
    if not xtick_modify:
        # just plot spec chunks, don't modify XTICK & XTICKLABELS
        for i in xrange(n_spec):
            # for each list of spec chunks, do these:
            spec_chunks = spec_chunk_list[i]
            for j in xrange(n_chunks):
                spec_chunk = spec_chunks[j]
                ax.plot(spec_chunk['wave'],
                        spec_chunk['flux'] + offset_specs[i],
                        *args, **kwargs)
        # set xlim
        # xlim = np.min(wave_intervals[:, 0]), np.max(wave_intervals[:, 1])
        # ax.set_xlim(xlim)
        wave_offset, xlim = None, None

    else:
        assert wave_intervals is not None
        # need to modify XTICK & XTICKLABELS

        # 1> calculate wave_offset
        wave_offset = _calculate_wave_offset(n_spec, n_chunks, wave_intervals,
                                             xtick_gap_fraction, xtick_start)
        # print 'wave_offset', wave_offset
        for i in xrange(n_spec):
            # for each list of spec chunks, do these:
            spec_chunks = spec_chunk_list[i]
            for j in xrange(n_chunks):
                spec_chunk = spec_chunks[j]
                ax.plot(spec_chunk['wave'] - wave_offset[j],  # wave_offset
                        spec_chunk['flux'] + offset_specs[i],
                        *args, **kwargs)

        # 2> calculate xtick position & labels
        xtick_pos, xtick_lab = _xtick_pos_lab(
            n_chunks, wave_centers, wave_intervals, wave_offset, xtick_step,
            xtick_format_str=xtick_format_str,
            xtick_label_type=xtick_label_type)

        # set xtick
        ax.set_xticks(xtick_pos)
        # set xticklabels
        ax.set_xticklabels(xtick_lab)
        # set xlim
        xlim = np.min(wave_intervals[:, 0].flatten()-wave_offset.flatten()),\
               np.max(wave_intervals[:, 1].flatten()-wave_offset.flatten())
        ax.set_xlim(xlim)

    return ax, spec_chunk_list, wave_offset, offset_specs, xlim

def spec_quick_view_fiducial(ax, offset_specs, x=None, *args, **kwargs):
    """
    Parameters
    ----------
    ax: axes
        the axes on which fiducial lines will be plot
    offset_specs: array
        the Y offset values
    x: two-element array
        default is ax.get_xlim()

    *args, **kwargs:
        other plot parameters

    """
    fiducial_levels = np.array(offset_specs).flatten() + 1
    fiducial_levels = fiducial_levels.reshape(1, -1)
    fiducial_levels = fiducial_levels.repeat(2, axis=0)
    if x is None:
        x = np.array(ax.get_xlim()).flatten().reshape(-1, 1)

    ax.plot(x, fiducial_levels, *args, **kwargs)

    return None

def test_spec_quick_view():
    # read BC03 catalog
    print('-------------------------------------------------------')
    print('@Cham: reading BC03 catalog ...')
    bc03cat = Table.read(
        '/home/cham/PycharmProjects/bopy/bopy/data/model_bc03/Base.BC03.N.csv')
    bc03dir = '/home/cham/PycharmProjects/bopy/bopy/data/model_bc03/'

    # prepare wave_intervals
    wave_intervals = [[5750., 5840.], [6254., 6344.]]

    # break spectra into chunks
    print('@Cham: loading BC03 spectra ...')
    spec_list = []
    for i in xrange(15):
        fp = bc03dir + bc03cat['specfile'][i]
        spec = np.loadtxt(fp)
        spec = Spec(Table(spec, names=['wave', 'flux']))
        # spec = norm_spec_pixel(spec, 6000.)
        # spec_chunks_list.append(
        #     spec.extract_chunk_wave_interval(wave_intervals))
        spec_list.append(spec)

    # plot spec chunks
    print('@Cham: ploting spec chunks using spec_quick_view() ...')
    fig = plt.figure('test spec_quick_view method', figsize=(10, 20))
    ax = fig.add_subplot(111)
    ax, spec_chunk_list, wave_offset, offset_specs, xlim = \
        spec_quick_view(ax,
                        spec_list,
                        norm_type='chunk_median',
                        q=0.90,
                        delta_lambda=100.,
                        wave_intervals=wave_intervals,
                        wave_centers=[5780., 6284.],
                        flux_amp=None,
                        xtick_modify=True,
                        xtick_step=5.,
                        xtick_gap_fraction=0.05,
                        xtick_format_str=('%.0f', '%.1f'),
                        xtick_label_type='wavelength',
                        offset_perspec=0.05,
                        verbose=True,
                        ls='-',
                        c='r')

    spec_quick_view_fiducial(ax, offset_specs, None, 'k--')

    # print spec_chunk_list[0][0]
    print('@Cham: saving figure ...')
    fig.savefig('/home/cham/PycharmProjects/bopy/bopy/data/test_spec_quick_view/test.pdf')
    print('@Cham: test spec_quick_view OK!')

    return 0

if __name__ == '__main__':
    test_spec_quick_view()

<|endoftext|>


<commit_before>
import atexit
import os
import sys
import time
import traceback

from werkzeug.serving import _iter_module_files

def broadcast_reload():
    from bokeh.server.app import bokeh_app
    if hasattr(bokeh_app, 'wsmanager'):
        bokeh_app.wsmanager.send('debug:debug', 'reload')

def _wait_for_edit(extra_files=[], interval=1):
    """Waits until one of the files we're using have changed
    """
    from itertools import chain
    mtimes = {}
    while 1:
        for filename in chain(_iter_module_files(), extra_files or ()):
            try:
                mtime = os.stat(filename).st_mtime
            except OSError:
                continue
            old_time = mtimes.get(filename)
            if old_time is None:
                mtimes[filename] = mtime
                continue
            elif mtime > old_time:
                return
        time.sleep(interval)

def robust_reloader(func):
    def wrapper(*args, **kwargs):
        atexit.register(broadcast_reload)
        try:
            print ('running robust reloader')
            func(*args, **kwargs)
        except KeyboardInterrupt:
            raise
        except Exception:
            """If in robust reload mode, gather all dependent files
            and wait until something has changed - and if so,
            exit(3) (that's what werkzeug looks for to determine
            whether or not to reload)
            """
            extra_files = []
            traceback.print_exc()
            exc_type, exc_value, exc_tb = sys.exc_info()
            tb = exc_tb
            while tb:
                filename = tb.tb_frame.f_code.co_filename
                extra_files.append(filename)
                tb = tb.tb_next
            if isinstance(exc_value, SyntaxError):
                extra_files.append(exc_value.filename)
            print ('wait for edit')
            _wait_for_edit(extra_files=extra_files)
            sys.exit(3)
    return wrapper
<commit_msg>Fix broken werkzeug import in bokeh-server<commit_after>
import atexit
import os
import sys
import time
import traceback

try:
    from werkzeug._reloader import _iter_module_files
except ImportError:
    # backward compat with old versions of werkzeug
    from werkzeug.serving import _iter_module_files

def broadcast_reload():
    from bokeh.server.app import bokeh_app
    if hasattr(bokeh_app, 'wsmanager'):
        bokeh_app.wsmanager.send('debug:debug', 'reload')

def _wait_for_edit(extra_files=[], interval=1):
    """Waits until one of the files we're using have changed
    """
    from itertools import chain
    mtimes = {}
    while 1:
        for filename in chain(_iter_module_files(), extra_files or ()):
            try:
                mtime = os.stat(filename).st_mtime
            except OSError:
                continue
            old_time = mtimes.get(filename)
            if old_time is None:
                mtimes[filename] = mtime
                continue
            elif mtime > old_time:
                return
        time.sleep(interval)

def robust_reloader(func):
    def wrapper(*args, **kwargs):
        atexit.register(broadcast_reload)
        try:
            print ('running robust reloader')
            func(*args, **kwargs)
        except KeyboardInterrupt:
            raise
        except Exception:
            """If in robust reload mode, gather all dependent files
            and wait until something has changed - and if so,
            exit(3) (that's what werkzeug looks for to determine
            whether or not to reload)
            """
            extra_files = []
            traceback.print_exc()
            exc_type, exc_value, exc_tb = sys.exc_info()
            tb = exc_tb
            while tb:
                filename = tb.tb_frame.f_code.co_filename
                extra_files.append(filename)
                tb = tb.tb_next
            if isinstance(exc_value, SyntaxError):
                extra_files.append(exc_value.filename)
            print ('wait for edit')
            _wait_for_edit(extra_files=extra_files)
            sys.exit(3)
    return wrapper
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# tetris.py version 0.1

class Cell(object):
    def __init__(self, row, col, char='.'):
        self.cell = char
        self.row = row
        self.col = col

    def get_cell_char(self):
        return self.cell

    def set_cell(self, char):
        self.cell = char

class Matrix(object):
    def __init__(self, cols=10, rows=22):
        self.cols = cols
        self.rows = rows
        self.matrix = [[Cell(row, col) for col in range(cols)] for row in range(rows)]

    def get_string_matrix(self):
        row_table = [elem.get_cell_char() for elem in item]
        return ''.join([' '.join(row_table) + '\n' for item in self.matrix])

    def set_matrix(self, input_file_name):
        try:
            with open(input_file_name) as fh:
                for row_nr, row in enumerate(fh):
                    for col_nr in range(0, len(row) - 1, 2):
                        if row[col_nr] != ' ':
                            self.matrix[row_nr][col_nr//2].set_cell(row[col_nr])
        except EnvironmentError as err:
            print(err)

    def clear_matrix(self):
        for row in range(self.rows):
            for col in range(self.cols):
                self.matrix[row][col].set_cell('.')

if __name__ == "__main__":

    m = Matrix()
    answer = input()

    # test 2
    if answer == 'p':
        print(m.get_string_matrix())

    # test 3
    if answer == 'g':
        m.set_matrix('input_test3.txt')
        print(m.get_string_matrix())

    # test 4
    if answer == 'c':
        m.clear_matrix()
        print(m.get_string_matrix())
<commit_msg>Printing funcion fixed<commit_after>#!/usr/bin/env python3
# tetris.py version 0.1

class Cell(object):
    def __init__(self, row, col, char='.'):
        self.cell = char
        self.row = row
        self.col = col

    def get_cell_char(self):
        return self.cell

    def set_cell(self, char):
        self.cell = char

class Matrix(object):
    def __init__(self, cols=10, rows=22):
        self.cols = cols
        self.rows = rows
        self.matrix = [[Cell(row, col) for col in range(cols)] for row in range(rows)]

    def get_string_matrix(self):
        return ''.join([' '.join([elem.get_cell_char() for elem in item]) + '\n' for item in self.matrix])

    def set_matrix(self, input_file_name):
        try:
            with open(input_file_name) as fh:
                for row_nr, row in enumerate(fh):
                    for col_nr in range(0, len(row) - 1, 2):
                        if row[col_nr] != ' ':
                            self.matrix[row_nr][col_nr//2].set_cell(row[col_nr])
        except EnvironmentError as err:
            print(err)

    def clear_matrix(self):
        for row in range(self.rows):
            for col in range(self.cols):
                self.matrix[row][col].set_cell('.')

if __name__ == "__main__":

    m = Matrix()
    answer = input()

    # test 2
    if answer == 'p':
        print(m.get_string_matrix())

    # test 3
    if answer == 'g':
        m.set_matrix('input_test3.txt')
        print(m.get_string_matrix())

    # test 4
    if answer == 'c':
        m.clear_matrix()
        print(m.get_string_matrix())
<|endoftext|>