Top

ladybugdynamo.sunpath module

from ladybug.sunpath import *
import geometryoperations as go

# import Dynamo libraries
try:
    import clr
    clr.AddReference('ProtoGeometry')
    from Autodesk.DesignScript.Geometry import *
except ImportError:
    print "Failed to import Dynamo libraries. Make sure path is added to sys.path"
class Sunpath(LBSunpath):
    """
    Calculates sun path for Dynamo

    Attributes:
        latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator.
        northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0)
        longitude: The longitude of the location (Default: 0)
        timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot.  The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West.
        daylightSavingPeriod: An analysis period for daylight saving. (Default = None)
        basePoint: A DS point that will be used as the center of sunpath. Default is Origin
        scale: A number larger than 0 for scale of sunpath
        sunScale: A number larger than 0 for scale of sun spheres

    Usage:
        import ladybugdynamo.epw as epw
        import ladybugdynamo.sunpath as sunpath

        # initiate sunpath based on location
        epwWeatherFile = r"c:\yourEpwFile.epw"
        location = epw.EPW(epwWeatherFile).location
        sp = sunpath.Sunpath.fromLocation(location)
        hourlyCurves = sp.draw()
    """

    def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
                daylightSavingPeriod = None, basePoint = None, scale = 1, sunScale = 1):

        # NOTE
        # The use of self.__class__ instead of name of subclass can be tricky in case of creating
        # a new subclass from sunpath unless it provides it's own init but it was the only way that I could
        # get it to work for now.
        # Read more here: http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods
        super(self.__class__, self).__init__(latitude, northAngle, longitude, timeZone, daylightSavingPeriod)

        if not basePoint: basePoint = Point.Origin()
        self.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)

        self.scale = float(scale) #scale of the whole sunpath
        self.sunScale = float(sunScale) * self.scale #scale for sun s
        self.__radius = 50
        self.__suns = [] # placeholder for sun(s)
        self.__dailyCurves = []
        self.__analemmaCurves = []
        self.__baseCurves = []

    @classmethod
    def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None, \
            basePoint = None, scale = 1, sunScale = 1):
        """Create sunpath from location data"""
        sp = super(Sunpath, cls).fromLocation(location, northAngle, daylightSavingPeriod)

        if not basePoint: basePoint = Point.Origin()
        sp.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)

        sp.scale = float(scale) #scale of the whole sunpath
        sp.sunScale = float(sunScale) * sp.scale #scale for sun s
        return sp

    @property
    def geometries(self):
        return {
        'dailyCurves': self.__dailyCurves,
        'analemmaCurves': self.__analemmaCurves,
        'baseCurves': self.__baseCurves,
        'suns': self.sunGeometries
        }

    @property
    def suns(self):
        """Get list of suns"""
        return self.__suns

    @property
    def sunGeometries(self):
        """Get list of suns"""
        if not len(self.__suns): return self.__suns #emapty list
        return [sun.geometry for sun in self.__suns]

    def removeSuns(self):
        self.__suns = []
        return True

        """Remove all suns from the sunpath"""

    @property
    def dailyCurves(self):
        """Get daily curves as a list"""
        return self.__dailyCurves

    @property
    def analemmaCurves(self):
        """Get daily curves as a list"""
        return self.__analemmaCurves

    @property
    def baseCurves(self):
        """Get daily curves as a list"""
        return self.__baseCurves

    def drawSun(self, month, day, hour, isSolarTime = False):
        """Draw a sun based on month, day and hour"""
        #create a dateTime to check the input
        sun = self.calculateSunPosition(month, day, hour, isSolarTime)

        if sun.isDuringDay: self.__suns.append(sun)

    def drawSunFromDateTime(self, dateTime, isSolarTime = False):
        """Draw a sun based on datetime"""
        self.drawSun(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

    def drawDailySunpath(self, month, day = 21):
        # draw curve for the day
        self.calculateDailyCurve(month, day)
        # draw baseline circles
        self.calculateBaseCurves()

    def drawAnnualSunpath(self):
        # draw analemma curves
        self.calculateAnalemmaCurves()

        # draw hourly curves
        self.calculateDailyCurves()

        # draw baseline circles
        self.calculateBaseCurves()

    def calculateSunPositionFromDateTime(self, dateTime, isSolarTime = False):
        """ Calculate the position of sun based on origin and scale

            Returns:
                sun: Returns a sun object
        """
        return self.calculateSunPosition(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

    def calculateSunPosition(self, month = 12, day = 21, hour = 12, isSolarTime = False):
        """ Calculate the position of sun based on origin and scale

            Returns:
                sun: A Ladybug sun object
        """
        # calculate ladybug sun for this time
        lbSun = self.calculateSun(month, day, hour, isSolarTime)

        # create a dynamo sun from ladybug sun
        sun = Sun.fromLBSun(lbSun, self.basePoint, self.__radius, self.scale, self.sunScale)

        return sun

    def calculateDailyCurve(self, month, day = 21, isSolarTime = False):
        """Calculate daily curve the day
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        # calculate sunrise, noon and sunset
        datetimesDictionary = self.calculateSunriseSunset(month, day = day, depression = 0, isSolarTime = isSolarTime)

        datetimes = [
                    datetimesDictionary['sunrise'], \
                    datetimesDictionary['noon'], \
                    datetimesDictionary['sunset']
                    ]

        dailySunPositions = []
        for datetime in datetimes:
            sun = self.calculateSunPositionFromDateTime(datetime, isSolarTime = False)
            dailySunPositions.append(sun.position)

        dailyCurve = Arc.ByThreePoints(*dailySunPositions)
        self.__dailyCurves.append(dailyCurve)
        go.disposeGeometries(dailySunPositions)

    def calculateDailyCurves(self):
        """Calculate daily curves for an annual sunpath
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        # calculate curves for 21st of all the months
        for month in range(1,13):
            self.calculateDailyCurve(month)

    def calculateAnalemmaCurves(self):
        """Calculate analemma curves for an annual sunpath
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        day = 21
        # point and plane for triming the curves
        pt = Point.ByCoordinates(0, 0, -10 + self.basePoint.Z)
        plane = Plane.ByOriginNormal(self.basePoint, Vector.ZAxis())

        for hour in range(1, 25):
            anySunUpHour = False
            anySunDownHour = False
            monthlySunPositions = []
            for month in range(1,13):
                sun = self.calculateSunPosition(month, day, hour = hour)
                if sun.isDuringDay:
                    anySunUpHour = True
                else:
                    anySunDownHour = True

                monthlySunPositions.append(sun.position)

            # all night hour
            if not anySunUpHour: continue
            # create the curve
            analemmaCurve = NurbsCurve.ByPoints(monthlySunPositions, True)

            if anySunDownHour + anySunUpHour == 2:
                # some of the hours are up hours and some are down
                # trim the curve
                curves = Geometry.Trim(analemmaCurve, plane, pt)
                # Dynamo trim doesn't work as expected
                # or I don't know how it is supposed to work so I check the curves
                selectedCurves = []
                for curve in curves:
                    # find mid point
                    midPar = (curve.EndParameter() + curve.StartParameter())/2
                    midPt = curve.PointAtParameter(midPar)
                    if midPt.Z >= self.basePoint.Z:
                        selectedCurves.append(curve)
                    else:
                        curve.Dispose()

                if len(selectedCurves)==1:
                    analemmaCurve = selectedCurves[0]
                else:
                    try:
                        # join curves
                        analemmaCurve = selectedCurves[0].Join(selectedCurves[1:])
                    except StandardError:
                        analemmaCurve = selectedCurves

            go.disposeGeometries(monthlySunPositions)

            self.__analemmaCurves.append(analemmaCurve)

    def calculateBaseCurves(self):
        """Calculate base circles for sunpath"""
        self.__drawBaseCircles()
        self.__drawDirectionLines()

    # TODO: Add north angle
    def __drawDirectionLines(self):
        """Draw direction lines"""
        # create North Arrow
        startVector = Vector.Scale(Vector.YAxis(), self.scale * self.__radius)
        startPt = Point.Add(self.basePoint, startVector)
        endVector = Vector.Scale(Vector.YAxis(), 1.12 * self.scale * self.__radius)
        endPt = Point.Add(self.basePoint, endVector)
        northArrow = Line.ByStartPointEndPoint(startPt, endPt)
        # draw it for the 4 direction
        for angle in range(0, 360, 90):
            self.__baseCurves.append(northArrow.Rotate(Plane.XY(), angle + self.northAngle))

        # create mid curves
        endVector = Vector.Scale(Vector.YAxis(), 1.08 * self.scale * self.__radius)
        endPt = Point.Add(self.basePoint, endVector)
        shortArrow = Line.ByStartPointEndPoint(startPt, endPt)
        # draw it for the 4 direction
        for angle in range(0, 360, 30):
            if angle%90!=0:
                self.__baseCurves.append(shortArrow.Rotate(Plane.XY(), angle + self.northAngle))

    def __drawBaseCircles(self):
        """draw base circles"""
        innerCircle = Circle.ByCenterPointRadius(self.basePoint, self.scale * self.__radius)
        outerCircle = Circle.ByCenterPointRadius(self.basePoint, 1.02 * self.scale * self.__radius)
        outerCircle2 = Circle.ByCenterPointRadius(self.basePoint, 1.08 * self.scale * self.__radius)
        self.__baseCurves.extend([outerCircle, innerCircle, outerCircle2])


class Sun(LBSun):
    """Convert a ladybug sun to a ladybug dynamo sun

    Attributes:
        datetime: A Ladybug datetime that represents the datetime for this sunVector
        altitude: Solar Altitude in radians
        azimuth: Solar Azimuth in radians
        isSolarTime: A Boolean that indicates if datetime represents the solar time
        isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period
        northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.
    """
    def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle,
                sunpathBasePoint, sunpathScale, sunpathRadius, sunScale):

        #LBSun.__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
        super(self.__class__, self).__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
        self.__sunpathBasePoint = sunpathBasePoint
        self.__sunpathScale = sunpathScale
        self.__sunpathRadius = sunpathRadius
        self.__sunRadius = 1
        self.scale = sunScale

        # calculate vector, position and geometry
        self.__calculateGeometricalSun()

        self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet

    # NOTE: I assume there should be cleaner solution to do this but I couldn't figure it out
    # Ladybug sun is called under the hood in ladybug.sunpath that's
    @classmethod
    def fromLBSun(cls, LBSun, basePoint, scale, radius, sunScale):
        return cls(LBSun.datetime, LBSun.altitudeInRadians, LBSun.azimuthInRadians, LBSun.isSolarTime, \
            LBSun.isDaylightSaving, LBSun.northAngle, basePoint, scale, radius, sunScale)

    def __calculateGeometricalSun(self):
        """calculate sun vector, base point and sphere"""
        # convert sun vector to Dynamo Vector
        self.__vector = Vector.ByCoordinates(self.sunVector.x, self.sunVector.y, self.sunVector.z)

        movingVector = self.__vector.Reverse().Scale(self.__sunpathRadius * self.__sunpathScale)
        # calculate position
        self.__position = self.__sunpathBasePoint.Add(movingVector)
        # create sun sphere
        self.__createSunSphere()

    def __createSunSphere(self):
        """Create the sun sphere"""
        sp = Sphere.ByCenterPointRadius(self.__position, self.scale * self.__sunRadius)
        self.__geometry = sp

    @property
    def vector(self):
        """get sun vector as a Vector"""
        return self.__vector

    @property
    def position(self):
        """get sun position as Point"""
        return self.__position

    @property
    def geometry(self):
        """get sun sphere"""
        return self.__geometry

Classes

class Sun

Convert a ladybug sun to a ladybug dynamo sun

Attributes: datetime: A Ladybug datetime that represents the datetime for this sunVector altitude: Solar Altitude in radians azimuth: Solar Azimuth in radians isSolarTime: A Boolean that indicates if datetime represents the solar time isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.

class Sun(LBSun):
    """Convert a ladybug sun to a ladybug dynamo sun

    Attributes:
        datetime: A Ladybug datetime that represents the datetime for this sunVector
        altitude: Solar Altitude in radians
        azimuth: Solar Azimuth in radians
        isSolarTime: A Boolean that indicates if datetime represents the solar time
        isDaylightSaving: A Boolean that indicates if datetime is calculated for Daylight saving period
        northAngle: North angle of the sunpath in Degrees. This will be only used to calculate the solar vector.
    """
    def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle,
                sunpathBasePoint, sunpathScale, sunpathRadius, sunScale):

        #LBSun.__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
        super(self.__class__, self).__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
        self.__sunpathBasePoint = sunpathBasePoint
        self.__sunpathScale = sunpathScale
        self.__sunpathRadius = sunpathRadius
        self.__sunRadius = 1
        self.scale = sunScale

        # calculate vector, position and geometry
        self.__calculateGeometricalSun()

        self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet

    # NOTE: I assume there should be cleaner solution to do this but I couldn't figure it out
    # Ladybug sun is called under the hood in ladybug.sunpath that's
    @classmethod
    def fromLBSun(cls, LBSun, basePoint, scale, radius, sunScale):
        return cls(LBSun.datetime, LBSun.altitudeInRadians, LBSun.azimuthInRadians, LBSun.isSolarTime, \
            LBSun.isDaylightSaving, LBSun.northAngle, basePoint, scale, radius, sunScale)

    def __calculateGeometricalSun(self):
        """calculate sun vector, base point and sphere"""
        # convert sun vector to Dynamo Vector
        self.__vector = Vector.ByCoordinates(self.sunVector.x, self.sunVector.y, self.sunVector.z)

        movingVector = self.__vector.Reverse().Scale(self.__sunpathRadius * self.__sunpathScale)
        # calculate position
        self.__position = self.__sunpathBasePoint.Add(movingVector)
        # create sun sphere
        self.__createSunSphere()

    def __createSunSphere(self):
        """Create the sun sphere"""
        sp = Sphere.ByCenterPointRadius(self.__position, self.scale * self.__sunRadius)
        self.__geometry = sp

    @property
    def vector(self):
        """get sun vector as a Vector"""
        return self.__vector

    @property
    def position(self):
        """get sun position as Point"""
        return self.__position

    @property
    def geometry(self):
        """get sun sphere"""
        return self.__geometry

Ancestors (in MRO)

  • Sun
  • ladybugdynamo.ladybug.sunpath.LBSun
  • __builtin__.object

Instance variables

var HOY

Return Hour of the year

var altitude

Return solar altitude in degrees

var altitudeInRadians

Return solar altitude in radians

var azimuth

Return solar azimuth in degrees

var azimuthInRadians

Return solar azimuth in radians

var datetime

Return datetime

var geometry

get sun sphere

var hourlyData

var isDaylightSaving

Return a Boolean that indicates is datetime is solar time

var isDuringDay

var isSolarTime

Return a Boolean that indicates is datetime is solar time

var northAngle

Return north angle for +YAxis

var position

get sun position as Point

var scale

var sunVector

Return sun vector for this sun Sun vector will face

var vector

get sun vector as a Vector

Methods

def __init__(

self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle, sunpathBasePoint, sunpathScale, sunpathRadius, sunScale)

def __init__(self, datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle,
            sunpathBasePoint, sunpathScale, sunpathRadius, sunScale):
    #LBSun.__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
    super(self.__class__, self).__init__(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, northAngle)
    self.__sunpathBasePoint = sunpathBasePoint
    self.__sunpathScale = sunpathScale
    self.__sunpathRadius = sunpathRadius
    self.__sunRadius = 1
    self.scale = sunScale
    # calculate vector, position and geometry
    self.__calculateGeometricalSun()
    self.__hourlyData = [] # Place holder for hourly data I'm not sure how it will work yet

def appendHourlyData(

self, data)

Append Ladybug hourly data to this sun

def appendHourlyData(self, data):
    """Append Ladybug hourly data to this sun"""
    assert data.datetime.HOY == self.HOY
    self.__hourlyData.append(data)
    return True

def fromLBSun(

cls, LBSun, basePoint, scale, radius, sunScale)

@classmethod
def fromLBSun(cls, LBSun, basePoint, scale, radius, sunScale):
    return cls(LBSun.datetime, LBSun.altitudeInRadians, LBSun.azimuthInRadians, LBSun.isSolarTime, \
        LBSun.isDaylightSaving, LBSun.northAngle, basePoint, scale, radius, sunScale)

class Sunpath

Calculates sun path for Dynamo

Attributes: latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator. northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0) longitude: The longitude of the location (Default: 0) timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot. The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West. daylightSavingPeriod: An analysis period for daylight saving. (Default = None) basePoint: A DS point that will be used as the center of sunpath. Default is Origin scale: A number larger than 0 for scale of sunpath sunScale: A number larger than 0 for scale of sun spheres

Usage: import ladybugdynamo.epw as epw import ladybugdynamo.sunpath as sunpath

# initiate sunpath based on location
epwWeatherFile = r"c:\yourEpwFile.epw"
location = epw.EPW(epwWeatherFile).location
sp = sunpath.Sunpath.fromLocation(location)
hourlyCurves = sp.draw()
class Sunpath(LBSunpath):
    """
    Calculates sun path for Dynamo

    Attributes:
        latitude: The latitude of the location. Values must be between -90 and 90. Default is set to the equator.
        northAngle: Angle to north (0-360). 90 is west and 270 is east (Default: 0)
        longitude: The longitude of the location (Default: 0)
        timeZone: A number representing the time zone of the location you are constructing. This can improve the accuracy of the resulting sun plot.  The time zone should follow the epw convention and should be between -12 and +12, where 0 is at Greenwich, UK, positive values are to the East of Greenwich and negative values are to the West.
        daylightSavingPeriod: An analysis period for daylight saving. (Default = None)
        basePoint: A DS point that will be used as the center of sunpath. Default is Origin
        scale: A number larger than 0 for scale of sunpath
        sunScale: A number larger than 0 for scale of sun spheres

    Usage:
        import ladybugdynamo.epw as epw
        import ladybugdynamo.sunpath as sunpath

        # initiate sunpath based on location
        epwWeatherFile = r"c:\yourEpwFile.epw"
        location = epw.EPW(epwWeatherFile).location
        sp = sunpath.Sunpath.fromLocation(location)
        hourlyCurves = sp.draw()
    """

    def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
                daylightSavingPeriod = None, basePoint = None, scale = 1, sunScale = 1):

        # NOTE
        # The use of self.__class__ instead of name of subclass can be tricky in case of creating
        # a new subclass from sunpath unless it provides it's own init but it was the only way that I could
        # get it to work for now.
        # Read more here: http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods
        super(self.__class__, self).__init__(latitude, northAngle, longitude, timeZone, daylightSavingPeriod)

        if not basePoint: basePoint = Point.Origin()
        self.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)

        self.scale = float(scale) #scale of the whole sunpath
        self.sunScale = float(sunScale) * self.scale #scale for sun s
        self.__radius = 50
        self.__suns = [] # placeholder for sun(s)
        self.__dailyCurves = []
        self.__analemmaCurves = []
        self.__baseCurves = []

    @classmethod
    def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None, \
            basePoint = None, scale = 1, sunScale = 1):
        """Create sunpath from location data"""
        sp = super(Sunpath, cls).fromLocation(location, northAngle, daylightSavingPeriod)

        if not basePoint: basePoint = Point.Origin()
        sp.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)

        sp.scale = float(scale) #scale of the whole sunpath
        sp.sunScale = float(sunScale) * sp.scale #scale for sun s
        return sp

    @property
    def geometries(self):
        return {
        'dailyCurves': self.__dailyCurves,
        'analemmaCurves': self.__analemmaCurves,
        'baseCurves': self.__baseCurves,
        'suns': self.sunGeometries
        }

    @property
    def suns(self):
        """Get list of suns"""
        return self.__suns

    @property
    def sunGeometries(self):
        """Get list of suns"""
        if not len(self.__suns): return self.__suns #emapty list
        return [sun.geometry for sun in self.__suns]

    def removeSuns(self):
        self.__suns = []
        return True

        """Remove all suns from the sunpath"""

    @property
    def dailyCurves(self):
        """Get daily curves as a list"""
        return self.__dailyCurves

    @property
    def analemmaCurves(self):
        """Get daily curves as a list"""
        return self.__analemmaCurves

    @property
    def baseCurves(self):
        """Get daily curves as a list"""
        return self.__baseCurves

    def drawSun(self, month, day, hour, isSolarTime = False):
        """Draw a sun based on month, day and hour"""
        #create a dateTime to check the input
        sun = self.calculateSunPosition(month, day, hour, isSolarTime)

        if sun.isDuringDay: self.__suns.append(sun)

    def drawSunFromDateTime(self, dateTime, isSolarTime = False):
        """Draw a sun based on datetime"""
        self.drawSun(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

    def drawDailySunpath(self, month, day = 21):
        # draw curve for the day
        self.calculateDailyCurve(month, day)
        # draw baseline circles
        self.calculateBaseCurves()

    def drawAnnualSunpath(self):
        # draw analemma curves
        self.calculateAnalemmaCurves()

        # draw hourly curves
        self.calculateDailyCurves()

        # draw baseline circles
        self.calculateBaseCurves()

    def calculateSunPositionFromDateTime(self, dateTime, isSolarTime = False):
        """ Calculate the position of sun based on origin and scale

            Returns:
                sun: Returns a sun object
        """
        return self.calculateSunPosition(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

    def calculateSunPosition(self, month = 12, day = 21, hour = 12, isSolarTime = False):
        """ Calculate the position of sun based on origin and scale

            Returns:
                sun: A Ladybug sun object
        """
        # calculate ladybug sun for this time
        lbSun = self.calculateSun(month, day, hour, isSolarTime)

        # create a dynamo sun from ladybug sun
        sun = Sun.fromLBSun(lbSun, self.basePoint, self.__radius, self.scale, self.sunScale)

        return sun

    def calculateDailyCurve(self, month, day = 21, isSolarTime = False):
        """Calculate daily curve the day
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        # calculate sunrise, noon and sunset
        datetimesDictionary = self.calculateSunriseSunset(month, day = day, depression = 0, isSolarTime = isSolarTime)

        datetimes = [
                    datetimesDictionary['sunrise'], \
                    datetimesDictionary['noon'], \
                    datetimesDictionary['sunset']
                    ]

        dailySunPositions = []
        for datetime in datetimes:
            sun = self.calculateSunPositionFromDateTime(datetime, isSolarTime = False)
            dailySunPositions.append(sun.position)

        dailyCurve = Arc.ByThreePoints(*dailySunPositions)
        self.__dailyCurves.append(dailyCurve)
        go.disposeGeometries(dailySunPositions)

    def calculateDailyCurves(self):
        """Calculate daily curves for an annual sunpath
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        # calculate curves for 21st of all the months
        for month in range(1,13):
            self.calculateDailyCurve(month)

    def calculateAnalemmaCurves(self):
        """Calculate analemma curves for an annual sunpath
            After calculating the curves check 'dailyCurves' property for curve geometries
        """
        day = 21
        # point and plane for triming the curves
        pt = Point.ByCoordinates(0, 0, -10 + self.basePoint.Z)
        plane = Plane.ByOriginNormal(self.basePoint, Vector.ZAxis())

        for hour in range(1, 25):
            anySunUpHour = False
            anySunDownHour = False
            monthlySunPositions = []
            for month in range(1,13):
                sun = self.calculateSunPosition(month, day, hour = hour)
                if sun.isDuringDay:
                    anySunUpHour = True
                else:
                    anySunDownHour = True

                monthlySunPositions.append(sun.position)

            # all night hour
            if not anySunUpHour: continue
            # create the curve
            analemmaCurve = NurbsCurve.ByPoints(monthlySunPositions, True)

            if anySunDownHour + anySunUpHour == 2:
                # some of the hours are up hours and some are down
                # trim the curve
                curves = Geometry.Trim(analemmaCurve, plane, pt)
                # Dynamo trim doesn't work as expected
                # or I don't know how it is supposed to work so I check the curves
                selectedCurves = []
                for curve in curves:
                    # find mid point
                    midPar = (curve.EndParameter() + curve.StartParameter())/2
                    midPt = curve.PointAtParameter(midPar)
                    if midPt.Z >= self.basePoint.Z:
                        selectedCurves.append(curve)
                    else:
                        curve.Dispose()

                if len(selectedCurves)==1:
                    analemmaCurve = selectedCurves[0]
                else:
                    try:
                        # join curves
                        analemmaCurve = selectedCurves[0].Join(selectedCurves[1:])
                    except StandardError:
                        analemmaCurve = selectedCurves

            go.disposeGeometries(monthlySunPositions)

            self.__analemmaCurves.append(analemmaCurve)

    def calculateBaseCurves(self):
        """Calculate base circles for sunpath"""
        self.__drawBaseCircles()
        self.__drawDirectionLines()

    # TODO: Add north angle
    def __drawDirectionLines(self):
        """Draw direction lines"""
        # create North Arrow
        startVector = Vector.Scale(Vector.YAxis(), self.scale * self.__radius)
        startPt = Point.Add(self.basePoint, startVector)
        endVector = Vector.Scale(Vector.YAxis(), 1.12 * self.scale * self.__radius)
        endPt = Point.Add(self.basePoint, endVector)
        northArrow = Line.ByStartPointEndPoint(startPt, endPt)
        # draw it for the 4 direction
        for angle in range(0, 360, 90):
            self.__baseCurves.append(northArrow.Rotate(Plane.XY(), angle + self.northAngle))

        # create mid curves
        endVector = Vector.Scale(Vector.YAxis(), 1.08 * self.scale * self.__radius)
        endPt = Point.Add(self.basePoint, endVector)
        shortArrow = Line.ByStartPointEndPoint(startPt, endPt)
        # draw it for the 4 direction
        for angle in range(0, 360, 30):
            if angle%90!=0:
                self.__baseCurves.append(shortArrow.Rotate(Plane.XY(), angle + self.northAngle))

    def __drawBaseCircles(self):
        """draw base circles"""
        innerCircle = Circle.ByCenterPointRadius(self.basePoint, self.scale * self.__radius)
        outerCircle = Circle.ByCenterPointRadius(self.basePoint, 1.02 * self.scale * self.__radius)
        outerCircle2 = Circle.ByCenterPointRadius(self.basePoint, 1.08 * self.scale * self.__radius)
        self.__baseCurves.extend([outerCircle, innerCircle, outerCircle2])

Ancestors (in MRO)

  • Sunpath
  • ladybugdynamo.ladybug.sunpath.LBSunpath
  • __builtin__.object

Instance variables

var analemmaCurves

Get daily curves as a list

var baseCurves

Get daily curves as a list

var basePoint

var dailyCurves

Get daily curves as a list

var geometries

var latitude

get latitude in degrees

var longitude

get latitude in degrees

var scale

var sunGeometries

Get list of suns

var sunScale

var suns

Get list of suns

Methods

def __init__(

self, latitude=0, northAngle=0, longitude=0, timeZone=0, daylightSavingPeriod=None, basePoint=None, scale=1, sunScale=1)

def __init__(self, latitude = 0, northAngle = 0, longitude = 0, timeZone = 0,
            daylightSavingPeriod = None, basePoint = None, scale = 1, sunScale = 1):
    # NOTE
    # The use of self.__class__ instead of name of subclass can be tricky in case of creating
    # a new subclass from sunpath unless it provides it's own init but it was the only way that I could
    # get it to work for now.
    # Read more here: http://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods
    super(self.__class__, self).__init__(latitude, northAngle, longitude, timeZone, daylightSavingPeriod)
    if not basePoint: basePoint = Point.Origin()
    self.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)
    self.scale = float(scale) #scale of the whole sunpath
    self.sunScale = float(sunScale) * self.scale #scale for sun s
    self.__radius = 50
    self.__suns = [] # placeholder for sun(s)
    self.__dailyCurves = []
    self.__analemmaCurves = []
    self.__baseCurves = []

def calculateAnalemmaCurves(

self)

Calculate analemma curves for an annual sunpath After calculating the curves check 'dailyCurves' property for curve geometries

def calculateAnalemmaCurves(self):
    """Calculate analemma curves for an annual sunpath
        After calculating the curves check 'dailyCurves' property for curve geometries
    """
    day = 21
    # point and plane for triming the curves
    pt = Point.ByCoordinates(0, 0, -10 + self.basePoint.Z)
    plane = Plane.ByOriginNormal(self.basePoint, Vector.ZAxis())
    for hour in range(1, 25):
        anySunUpHour = False
        anySunDownHour = False
        monthlySunPositions = []
        for month in range(1,13):
            sun = self.calculateSunPosition(month, day, hour = hour)
            if sun.isDuringDay:
                anySunUpHour = True
            else:
                anySunDownHour = True
            monthlySunPositions.append(sun.position)
        # all night hour
        if not anySunUpHour: continue
        # create the curve
        analemmaCurve = NurbsCurve.ByPoints(monthlySunPositions, True)
        if anySunDownHour + anySunUpHour == 2:
            # some of the hours are up hours and some are down
            # trim the curve
            curves = Geometry.Trim(analemmaCurve, plane, pt)
            # Dynamo trim doesn't work as expected
            # or I don't know how it is supposed to work so I check the curves
            selectedCurves = []
            for curve in curves:
                # find mid point
                midPar = (curve.EndParameter() + curve.StartParameter())/2
                midPt = curve.PointAtParameter(midPar)
                if midPt.Z >= self.basePoint.Z:
                    selectedCurves.append(curve)
                else:
                    curve.Dispose()
            if len(selectedCurves)==1:
                analemmaCurve = selectedCurves[0]
            else:
                try:
                    # join curves
                    analemmaCurve = selectedCurves[0].Join(selectedCurves[1:])
                except StandardError:
                    analemmaCurve = selectedCurves
        go.disposeGeometries(monthlySunPositions)
        self.__analemmaCurves.append(analemmaCurve)

def calculateBaseCurves(

self)

Calculate base circles for sunpath

def calculateBaseCurves(self):
    """Calculate base circles for sunpath"""
    self.__drawBaseCircles()
    self.__drawDirectionLines()

def calculateDailyCurve(

self, month, day=21, isSolarTime=False)

Calculate daily curve the day After calculating the curves check 'dailyCurves' property for curve geometries

def calculateDailyCurve(self, month, day = 21, isSolarTime = False):
    """Calculate daily curve the day
        After calculating the curves check 'dailyCurves' property for curve geometries
    """
    # calculate sunrise, noon and sunset
    datetimesDictionary = self.calculateSunriseSunset(month, day = day, depression = 0, isSolarTime = isSolarTime)
    datetimes = [
                datetimesDictionary['sunrise'], \
                datetimesDictionary['noon'], \
                datetimesDictionary['sunset']
                ]
    dailySunPositions = []
    for datetime in datetimes:
        sun = self.calculateSunPositionFromDateTime(datetime, isSolarTime = False)
        dailySunPositions.append(sun.position)
    dailyCurve = Arc.ByThreePoints(*dailySunPositions)
    self.__dailyCurves.append(dailyCurve)
    go.disposeGeometries(dailySunPositions)

def calculateDailyCurves(

self)

Calculate daily curves for an annual sunpath After calculating the curves check 'dailyCurves' property for curve geometries

def calculateDailyCurves(self):
    """Calculate daily curves for an annual sunpath
        After calculating the curves check 'dailyCurves' property for curve geometries
    """
    # calculate curves for 21st of all the months
    for month in range(1,13):
        self.calculateDailyCurve(month)

def calculateSun(

self, month, day, hour, isSolarTime=False)

Get Sun data for an hour of the year

Args: month: An integer between 1-12 day: An integer between 1-31 hour: A positive number <= 24 isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)

Returns: A sun object for this particular time

def calculateSun(self, month, day, hour, isSolarTime = False):
    """Get Sun data for an hour of the year
        Args:
            month: An integer between 1-12
            day: An integer between 1-31
            hour: A positive number <= 24
            isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
        Returns:
            A sun object for this particular time
    """
    datetime = core.LBDateTime(month, day, hour)
    return self.calculateSunFromDataTime(datetime, isSolarTime)

def calculateSunFromDataTime(

self, datetime, isSolarTime=False)

Get Sun data for an hour of the year This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com) based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari

Args: datetime: Ladybug datetime isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)

Returns: A sun object for this particular time

def calculateSunFromDataTime(self, datetime, isSolarTime = False):
    """Get Sun data for an hour of the year
        This code is originally written by Trygve Wastvedt (Trygve.Wastvedt@gmail.com)
        based on (NOAA) and modified by Chris Mackey and Mostapha Roudsari
        Args:
            datetime: Ladybug datetime
            isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
        Returns:
            A sun object for this particular time
    """
    solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
    month, day, hour = datetime.month, datetime.day, datetime.floatHour
    isDaylightSaving = self.isDaylightSavingHour(datetime.HOY)
    if isDaylightSaving: hour += 1
    #hours
    solTime = self.__calculateSolarTime(hour, eqOfTime, isSolarTime)
    #degrees
    hourAngle = (solTime*15 + 180) if (solTime*15 < 0) else (solTime*15 - 180)
    #RADIANS
    zenith = math.acos(math.sin(self.__latitude)*math.sin(solDec) \
        + math.cos(self.__latitude)*math.cos(solDec)*math.cos(math.radians(hourAngle)))
    altitude = (math.pi/2) - zenith
    if hourAngle == 0.0 or hourAngle == -180.0 or hourAngle == 180.0:
        if solDec < self.__latitude: azimuth = math.pi
        else: azimuth = 0.0
    else:
        azimuth = ((math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
            - math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith))) + math.pi) % (2*math.pi)) \
            if (hourAngle > 0) else \
                ((3*math.pi - math.acos(((math.sin(self.__latitude)*math.cos(zenith)) \
                - math.sin(solDec))/(math.cos(self.__latitude)*math.sin(zenith)))) % (2*math.pi))
    # create the sun for this hour
    return LBSun(datetime, altitude, azimuth, isSolarTime, isDaylightSaving, self.northAngle)

def calculateSunFromHOY(

self, HOY, isSolarTime=False)

Get Sun data for an hour of the year

Args: datetime: Ladybug datetime isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)

Returns: A sun object for this particular time

def calculateSunFromHOY(self, HOY, isSolarTime = False):
    """Get Sun data for an hour of the year
        Args:
            datetime: Ladybug datetime
            isSolarTime: A boolean to indicate if the input hour is solar time. (Default: False)
        Returns:
            A sun object for this particular time
    """
    datetime = core.LBDateTime.fromHOY(HOY)
    return self.calculateSunFromDataTime(datetime, isSolarTime)

def calculateSunPosition(

self, month=12, day=21, hour=12, isSolarTime=False)

Calculate the position of sun based on origin and scale

Returns: sun: A Ladybug sun object

def calculateSunPosition(self, month = 12, day = 21, hour = 12, isSolarTime = False):
    """ Calculate the position of sun based on origin and scale
        Returns:
            sun: A Ladybug sun object
    """
    # calculate ladybug sun for this time
    lbSun = self.calculateSun(month, day, hour, isSolarTime)
    # create a dynamo sun from ladybug sun
    sun = Sun.fromLBSun(lbSun, self.basePoint, self.__radius, self.scale, self.sunScale)
    return sun

def calculateSunPositionFromDateTime(

self, dateTime, isSolarTime=False)

Calculate the position of sun based on origin and scale

Returns: sun: Returns a sun object

def calculateSunPositionFromDateTime(self, dateTime, isSolarTime = False):
    """ Calculate the position of sun based on origin and scale
        Returns:
            sun: Returns a sun object
    """
    return self.calculateSunPosition(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

def calculateSunriseSunset(

self, month, day, depression=0.833, isSolarTime=False)

def calculateSunriseSunset(self, month, day, depression = 0.833, isSolarTime = False):
    datetime = core.LBDateTime(month, day, hour = 12)
    return self.calculateSunriseSunsetFromDateTime(datetime, depression, isSolarTime)

def calculateSunriseSunsetFromDateTime(

self, datetime, depression=0.833, isSolarTime=False)

Calculate sunrise, sunset and noon for a day of year

def calculateSunriseSunsetFromDateTime(self, datetime, depression = 0.833, isSolarTime = False):
    """Calculate sunrise, sunset and noon for a day of year"""
    solDec, eqOfTime = self.__calculateSolarGeometry(datetime)
    # calculate sunrise and sunset hour
    #if isSolarTime:
    #    noon = .5
    #else:
    noon = (720 - 4 * math.degrees(self.__longitude) - eqOfTime + self.timeZone * 60) / 1440
    sunRiseHourAngle = self.__calculateSunriseHourAngle(solDec, depression)
    sunrise  = noon - sunRiseHourAngle * 4 / 1440
    sunset   = noon + sunRiseHourAngle * 4 / 1440
    # convert demical hour to solar hour
    # noon    = self.__calculateSolarTime(24*noon, eqOfTime, isSolarTime)
    # sunrise = self.__calculateSolarTime(24*sunrise, eqOfTime, isSolarTime)
    # sunset  = self.__calculateSolarTime(24*sunset, eqOfTime, isSolarTime)
    return {
            "sunrise"   : core.LBDateTime(datetime.month, datetime.day, 24 * sunrise),
            "noon"      : core.LBDateTime(datetime.month, datetime.day, 24 * noon),
            "sunset"    : core.LBDateTime(datetime.month, datetime.day, 24 * sunset)}

def drawAnnualSunpath(

self)

def drawAnnualSunpath(self):
    # draw analemma curves
    self.calculateAnalemmaCurves()
    # draw hourly curves
    self.calculateDailyCurves()
    # draw baseline circles
    self.calculateBaseCurves()

def drawDailySunpath(

self, month, day=21)

def drawDailySunpath(self, month, day = 21):
    # draw curve for the day
    self.calculateDailyCurve(month, day)
    # draw baseline circles
    self.calculateBaseCurves()

def drawSun(

self, month, day, hour, isSolarTime=False)

Draw a sun based on month, day and hour

def drawSun(self, month, day, hour, isSolarTime = False):
    """Draw a sun based on month, day and hour"""
    #create a dateTime to check the input
    sun = self.calculateSunPosition(month, day, hour, isSolarTime)
    if sun.isDuringDay: self.__suns.append(sun)

def drawSunFromDateTime(

self, dateTime, isSolarTime=False)

Draw a sun based on datetime

def drawSunFromDateTime(self, dateTime, isSolarTime = False):
    """Draw a sun based on datetime"""
    self.drawSun(dateTime.month, dateTime.day, dateTime.floatHour, isSolarTime)

def fromLocation(

cls, location, northAngle=0, daylightSavingPeriod=None, basePoint=None, scale=1, sunScale=1)

Create sunpath from location data

@classmethod
def fromLocation(cls, location, northAngle = 0, daylightSavingPeriod = None, \
        basePoint = None, scale = 1, sunScale = 1):
    """Create sunpath from location data"""
    sp = super(Sunpath, cls).fromLocation(location, northAngle, daylightSavingPeriod)
    if not basePoint: basePoint = Point.Origin()
    sp.basePoint = Point.ByCoordinates(basePoint.X, basePoint.Y, basePoint.Z)
    sp.scale = float(scale) #scale of the whole sunpath
    sp.sunScale = float(sunScale) * sp.scale #scale for sun s
    return sp

def isDaylightSavingHour(

self, datetime)

def isDaylightSavingHour(self, datetime):
    if not self.daylightSavingPeriod: return False
    return self.daylightSavingPeriod.isTimeIncluded(datetime.HOY)

def removeSuns(

self)

def removeSuns(self):
    self.__suns = []
    return True
    """Remove all suns from the sunpath"""