Show Current Sun Position and Shadow of House (Generate SVG)

@sjef86, @FredericMa

Works for me! Thanks

But I had to make a few changes:

Import section:

from __future__ import print_function

import math
# import time
from datetime import datetime, timedelta, date, time
import sys
import pytz
from astral import Astral
from astral import Location

Init function

	def __init__(self):

		self.debug = False 
		self.oh = openhab()
		self.astr = Astral()
		timezone = pytz.timezone('Europe/London')
		self.l = Location(('HOME', 'WATFORD', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
		self.sun = self.l.sun()
		now = timezone.localize(datetime.now())
		self.azimuth = float(self.astr.solar_azimuth(now, LATITUDE, LONGITUDE))
		self.elevation = float(self.astr.solar_elevation(now, LATITUDE, LONGITUDE))
		self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
		self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
		for i in xrange(0,24,HOURS):
			a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))

Main function (At the bottom)

	t1 = datetime.now()
...
	t2 = datetime.now()

Note that I have removed the print statements
Change 'Europe/London' with 'London/Olso' as required
Did you check that pytz was installed?
sudo pip install pytz
Did you add the statement import pytz at the top of the script?

1 Like

Woeps, forgot those 2 indeed… I already got rid of the time import since I removed the duration calculation a while ago.
@sjef86 have you copied the exact code? Maybe something went wrong while copying and pasting?

@FredericMa
Hi there, thanks for you good find indeed!!
I copied the code manually, I don’t like copying and pasting because you don’t get inside the code and get to learn and understand it.
I got it to work after a few tries.

@sjef86
With python be very careful with trailing spaces at the end of lines and with your indents

Thanks again…

Thanks guys! I finally got it working here :slight_smile: having some indent problems after copy/paste. I added:

	t1 = datetime.now()
...
	t2 = datetime.now()

And now everything is working great!! :smiley:
Thanks once more

Br
Chris

diffToFasade was bedeuted das genau? Was muss ich eintragen ? Eine haus seite zeigt genau nach norden bei mir.

1 Like

The whole code without persistence and timezone corrected
You will need the Astral library (pip install Astral) and the pytz library (sudo pip install pytz)
svg generated is in the shared html folder

from __future__ import print_function

import math
# import time
from datetime import datetime, timedelta, date, time
import sys
import pytz
from astral import Astral
from astral import Location

from myopenhab import openhab
from myopenhab import mapValues
from myopenhab import getJSONValue


WIDTH = 100
HEIGHT = 100
PRIMARY_COLOR = '#1b3024'
LIGHT_COLOR = '#26bf75'
STROKE_WIDTH = '1'
FILENAME = '/etc/openhab2/html/shaddow.svg'
LATITUDE = XX.XXXXXX # Your Latitude
LONGITUDE = XX.XXXXX # Your Longitude
ALTITUDE = XXX # Your Altitude

# Shape of the house in a 100 by 100 units square

SHAPE = [{'x': 23.18, 'y': 19.35}, \
		{'x': 70.11, 'y': 15.53}, \
		{'x': 75.32, 'y': 52.49}, \
		{'x': 79.96, 'y': 52.38}, \
		{'x': 80.65, 'y': 65.59}, \
		{'x': 76.83, 'y': 66.05}, \
		{'x': 78.68, 'y': 77.64}, \
		{'x': 31.52, 'y': 82.16}, \
		{'x': 30.13, 'y': 73.24}, \
		{'x': 18.54, 'y': 74.63}, \
		{'x': 14.72, 'y': 46.70}, \
		{'x': 26.07, 'y': 44.61}]

HOURS = 1
DEGS = []

class shaddow(object):
	"""
	Shaddow Object
	"""
	def __init__(self):

		self.debug = False 
		self.oh = openhab()
		self.astr = Astral()
		timezone = pytz.timezone('Europe/London') # Enter your time zone
		self.l = Location(('HOME', 'TOWN', LATITUDE, LONGITUDE, 'Europe/London', ALTITUDE))
		self.sun = self.l.sun()
		now = timezone.localize(datetime.now())
		self.azimuth = float(self.astr.solar_azimuth(now, LATITUDE, LONGITUDE))
		print(self.azimuth)
		self.elevation = float(self.astr.solar_elevation(now, LATITUDE, LONGITUDE))
		print(self.elevation)
		self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
		self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
		for i in xrange(0, 24, HOURS):
			a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))
			if (a == None): a = 0
			DEGS.extend([float(a)])

	def generatePath(self,stroke,fill,points,attrs=None):

		p = ''
		p = p + '<path stroke="' + stroke + '" stroke-width="' + STROKE_WIDTH + '" fill="' + fill + '" '
		if (attrs != None): p = p + ' ' + attrs + ' '
		p = p + ' d="'
		for point in points:
			if (points.index(point) == 0):
				p = p + 'M' + str(point['x']) + ' ' + str(point['y'])
			else:
				p = p + ' L' + str(point['x']) + ' ' + str(point['y'])
		p = p + '" />'

		return p

	def generateArc(self,dist,stroke,start,end,attrs=None):

		p = ''
		try:
			angle = end-start
			if (angle<0):
				angle = 360 + angle

			p = p + '<path d="M' + str(self.degreesToPoint(start,dist)['x']) + ' ' + str(self.degreesToPoint(start,dist)['y']) + ' '
			p = p + 'A' + str(dist) + ' ' + str(dist) + ' 0 '
			if (angle<180):
				p = p + '0 1 '
			else:
				p = p + '1 1 '
			p = p + str(self.degreesToPoint(end,dist)['x']) + ' ' + str(self.degreesToPoint(end,dist)['y']) + '"'
			p = p + ' stroke="' + stroke + '"'
			if (attrs != None): 
				p = p + ' ' + attrs + ' '
			else:
				p = p + ' stroke-width="' + STROKE_WIDTH + '" fill="none" '
			p = p + ' />'
		except:
			p = ''

		return p	

	def degreesToPoint(self,d,r):

		coordinates = {'x': 0, 'y': 0}
		cx = WIDTH / 2
		cy = HEIGHT / 2 
		d2 = 180 - d
		coordinates['x'] = cx + math.sin(math.radians(d2))*r
		coordinates['y'] = cy + math.cos(math.radians(d2))*r

		return coordinates

	def generateSVG(self):

		realSun = self.degreesToPoint(self.azimuth, 10000)
		if self.debug:
			print(realSun)
			
		sun = self.degreesToPoint(self.azimuth, WIDTH / 2)

		minPoint = -1
		maxPoint = -1

		i = 0

		minAngle = 999
		maxAngle = -999
		for point in SHAPE:
			#Angle of close light source
			angle = -math.degrees(math.atan2(point['y']-sun['y'],point['x']-sun['x']))
			#Angle of distant light source (e.g. sun)
			angle = -math.degrees(math.atan2(point['y']-realSun['y'],point['x']-realSun['x']))
			distance = math.sqrt(math.pow(sun['y']-point['y'],2) + math.pow(sun['x']-point['x'],2))
			if (angle<minAngle): 
				minAngle = angle
				minPoint = i
			if (angle>maxAngle): 
				maxAngle = angle
				maxPoint = i
			point['angle'] = angle
			point['distance'] = distance
			if self.debug:
				print(str(i).ljust(10),":", str(point['x']).ljust(10), str(point['y']).ljust(10), str(round(angle,7)).ljust(10), str(round(distance)).ljust(10))
			i = i + 1

		if self.debug: 
			print("Min Point = ",minPoint)
			print("Max Point = ",maxPoint)
			print("")

		i = minPoint
		k = 0
		side1Distance = 0
		side2Distance = 0
		side1Done = False
		side2Done = False
		side1 = []
		side2 = []
		while True:
			if (side1Done == False):
				side1Distance = side1Distance + SHAPE[i]['distance']
				if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 1
				if (i == maxPoint): side1Done = True
				side1.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )
			if (side1Done == True):
				side2Distance = side2Distance + SHAPE[i]['distance']
				if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 2
				if (i == minPoint): side2Done = True
				side2.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )

			i = i + 1
			if( i > len(SHAPE)-1): i = 0

			if (side1Done and side2Done): break

			k = k + 1
			if (k == 20): break

		svg = '<?xml version="1.0" encoding="utf-8"?>'
		svg = svg + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
		svg = svg + '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-10 -10 120 120" xml:space="preserve">'

		minPointShaddowX = SHAPE[minPoint]['x'] + WIDTH * math.cos(math.radians(minAngle))
		minPointShaddowY = SHAPE[minPoint]['y'] - HEIGHT * math.sin(math.radians(minAngle))
		maxPointShaddowX = SHAPE[maxPoint]['x'] + WIDTH * math.cos(math.radians(maxAngle))
		maxPointShaddowY = SHAPE[maxPoint]['y'] - HEIGHT * math.sin(math.radians(maxAngle))

		shaddow = [ {'x': maxPointShaddowX, 'y': maxPointShaddowY } ] + \
				side2 + \
				[ {'x': minPointShaddowX, 'y': minPointShaddowY } ]
		
		svg = svg + '<defs><mask id="shaddowMask">'
		svg = svg + '	  <rect width="100%" height="100%" fill="black"/>'
		svg = svg + '	  <circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="white"/>'
		svg = svg + '</mask></defs>'

		svg = svg + self.generatePath('none',PRIMARY_COLOR,SHAPE)

		shaddow_svg = self.generatePath('none','black',shaddow,'mask="url(#shaddowMask)" fill-opacity="0.5"')

		if (self.elevation>0): 
			svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
		else:
			svg = svg + self.generatePath(PRIMARY_COLOR,'none',side1)

		if (self.elevation>0): svg = svg + shaddow_svg

		svg = svg + self.generateArc(WIDTH/2,PRIMARY_COLOR,self.sunset_azimuth,self.sunrise_azimuth)
		svg = svg + self.generateArc(WIDTH/2,LIGHT_COLOR,self.sunrise_azimuth,self.sunset_azimuth)

		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunrise_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunrise_azimuth,WIDTH/2+2)])
		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunset_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunset_azimuth,WIDTH/2+2)])

		for i in range(0,len(DEGS)):
			if (i == len(DEGS)-1):
				j = 0
			else:
				j = i + 1
			if (i % 2 == 0):
				svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,DEGS[i],DEGS[j],'stroke-width="3" fill="none" stroke-opacity="0.2"')	
			else:
				svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,DEGS[i],DEGS[j],'stroke-width="3" fill="none"')

		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[0],WIDTH/2+5), self.degreesToPoint(DEGS[0],WIDTH/2+11)])
		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+5), self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+11)])

		svg = svg + '<circle cx="' + str(sun['x']) + '" cy="' + str(sun['y']) + '" r="3" stroke="' + LIGHT_COLOR + '" stroke-width="' + STROKE_WIDTH + '" fill="' + LIGHT_COLOR + '" />'

		svg = svg + '</svg>'

		if self.debug:
			print(svg)

		f = open(FILENAME, 'w')
		f.write(svg)
		f.close()


def main():

	t1 = datetime.now()

	s = shaddow()

	args = sys.argv
		
	if(len(args) == 1):
		dummy = 0
		#print('\033[91mNo parameters specified\033[0;0m')
	else:
		if(args[1] == "update"):
			s.generateSVG()

	t2 = datetime.now()
	#print("Done in " + str(t2-t1) + " seconds")

if __name__ == '__main__':
	main()
1 Like

Hi,
I added the moon with an approx drawing of its phase. It needs the pylunar library (sudo pip install pylunar).
I also added a background color to have a better shadow view and beveled the sun drawing.

from __future__ import print_function

import math
# import time
from datetime import datetime, timedelta, date, time
import sys
import pytz
import pylunar

from astral import Astral
from astral import Location

from myopenhab import openhab
from myopenhab import mapValues
from myopenhab import getJSONValue


WIDTH = 100
HEIGHT = 100
PRIMARY_COLOR = '#1b3024'
LIGHT_COLOR = '#26bf75'
BG_COLOR = '#555555'
SUN_COLOR = '#ffff66'
SUN_RADIUS = 5

MOON_COLOR = '#999999'
MOON_RADIUS = 3
STROKE_WIDTH = '1'
FILENAME = '/etc/openhab2/html/shaddow.svg'
LATITUDE = XXX
LONGITUDE = XXX
ALTITUDE = XXX

# Shape of the house in a 100 by 100 units square

SHAPE = [{'x': 14.00637, 'y': 26.779708}, \
		{'x': 25.393663, 'y': 51.717879}, \
		{'x': 31.542801, 'y': 49.098802}, \
		{'x': 48.62374, 'y': 86.79074}, \
		{'x': 58.530685, 'y': 82.008077}, \
		{'x': 63.313348, 'y': 91.231785}, \
		{'x': 88.479264, 'y': 79.161254}, \
		{'x': 76.978099, 'y': 53.3121}, \
		{'x': 73.266786, 'y': 54.804337}, \
		{'x': 69.74343, 'y': 48.223985}, \
		{'x': 65.992327, 'y': 49.584331}, \
		{'x': 48.698439, 'y': 11.044762}]


	
HOURS = 1
DEGS = []

class shadow(object):
	"""
	Shadow Object
	"""
	def __init__(self):

		self.debug = False 
		self.oh = openhab()
		self.astr = Astral()
		timezone = pytz.timezone('Europe/Paris') # Enter your time zone
		self.l = Location(('HOME', 'YOUR TOWN', LATITUDE, LONGITUDE, 'Europe/Paris', ALTITUDE))
		self.sun = self.l.sun()
		self.now = timezone.localize(datetime.now())
		self.sun_azimuth = float(self.astr.solar_azimuth(self.now, LATITUDE, LONGITUDE))
		print('Sun azimuth: ' + str(self.sun_azimuth))
		self.sun_elevation = float(self.astr.solar_elevation(self.now, LATITUDE, LONGITUDE))
		print('Sun elevation: ' + str(self.sun_elevation))

		self.sunrise_azimuth = float(self.astr.solar_azimuth(self.sun['sunrise'], LATITUDE, LONGITUDE))
		self.sunset_azimuth = float(self.astr.solar_azimuth(self.sun['sunset'], LATITUDE, LONGITUDE))
		for i in xrange(0, 24, HOURS):
			a = float(self.astr.solar_azimuth(timezone.localize(datetime.combine(date.today(), time(i))), LATITUDE, LONGITUDE))
			if (a == None): a = 0
			DEGS.extend([float(a)])

		self.moon_info = pylunar.MoonInfo(self.decdeg2dms(LATITUDE), self.decdeg2dms(LONGITUDE))
		self.moon_info.update(self.now)
		self.moon_azimuth = self.moon_info.azimuth()
		print('Moon azimuth: ' + str(self.moon_azimuth))
		self.moon_elevation = self.moon_info.altitude()
		print('Moon elevation: ' + str(self.moon_elevation))

		if (self.sun_elevation>0): 
			self.elevation = self.sun_elevation
		else:
			self.elevation = self.moon_elevation


	#
	#
	#
	def decdeg2dms(self,dd):
		negative = dd < 0
		dd = abs(dd)
		minutes,seconds = divmod(dd*3600,60)
		degrees,minutes = divmod(minutes,60)
		if negative:
			if degrees > 0:
				degrees = -degrees
			elif minutes > 0:
				minutes = -minutes
			else:
				seconds = -seconds
		return (degrees,minutes,seconds)	

	#
	#
	#
	def generatePath(self,stroke,fill,points,attrs=None):

		p = ''
		p = p + '<path stroke="' + stroke + '" stroke-width="' + STROKE_WIDTH + '" fill="' + fill + '" '
		if (attrs != None): p = p + ' ' + attrs + ' '
		p = p + ' d="'
		for point in points:
			if (points.index(point) == 0):
				p = p + 'M' + str(point['x']) + ' ' + str(point['y'])
			else:
				p = p + ' L' + str(point['x']) + ' ' + str(point['y'])
		p = p + '" />'

		return p

	#
	#
	#
	def generateArc(self,dist,stroke,fill,start,end,attrs=None):

		p = ''
		try:
			angle = end-start
			if (angle<0):
				angle = 360 + angle

			p = p + '<path d="M' + str(self.degreesToPoint(start,dist)['x']) + ' ' + str(self.degreesToPoint(start,dist)['y']) + ' '
			p = p + 'A' + str(dist) + ' ' + str(dist) + ' 0 '
			if (angle<180):
				p = p + '0 1 '
			else:
				p = p + '1 1 '
			p = p + str(self.degreesToPoint(end,dist)['x']) + ' ' + str(self.degreesToPoint(end,dist)['y']) + '"'
			p = p + ' stroke="' + stroke + '"'
			if (fill != None): 
				p = p + ' fill="' + fill + '" '
			else:
				p = p + ' fill="none" '
			if (attrs != None): 
				p = p + ' ' + attrs + ' '
			else:
				p = p + ' stroke-width="' + STROKE_WIDTH + '"'
			p = p + ' />'
		except:
			p = ''

		return p	

	#
	#
	#
	def degreesToPoint(self,d,r):

		coordinates = {'x': 0, 'y': 0}
		cx = WIDTH / 2
		cy = HEIGHT / 2 
		d2 = 180 - d
		coordinates['x'] = cx + math.sin(math.radians(d2))*r
		coordinates['y'] = cy + math.cos(math.radians(d2))*r

		return coordinates

	#
	#
	#
	def generateSVG(self):

		realSun_pos = self.degreesToPoint(self.sun_azimuth, 10000)
		if self.debug:
			print(realSun_pos)
			
		sun_pos = self.degreesToPoint(self.sun_azimuth, WIDTH / 2)
		moon_pos = self.degreesToPoint(self.moon_azimuth, WIDTH / 2)

		minPoint = -1
		maxPoint = -1

		i = 0

		minAngle = 999
		maxAngle = -999
		for point in SHAPE:
			#Angle of close light source
			angle = -math.degrees(math.atan2(point['y']-sun_pos['y'],point['x']-sun_pos['x']))
			#Angle of distant light source (e.g. sun_pos)
			angle = -math.degrees(math.atan2(point['y']-realSun_pos['y'],point['x']-realSun_pos['x']))
			distance = math.sqrt(math.pow(sun_pos['y']-point['y'],2) + math.pow(sun_pos['x']-point['x'],2))
			if (angle<minAngle): 
				minAngle = angle
				minPoint = i
			if (angle>maxAngle): 
				maxAngle = angle
				maxPoint = i
			point['angle'] = angle
			point['distance'] = distance
			if self.debug:
				print(str(i).ljust(10),":", str(point['x']).ljust(10), str(point['y']).ljust(10), str(round(angle,7)).ljust(10), str(round(distance)).ljust(10))
			i = i + 1

		if self.debug: 
			print("Min Point = ",minPoint)
			print("Max Point = ",maxPoint)
			print("")

		i = minPoint
		k = 0
		side1Distance = 0
		side2Distance = 0
		side1Done = False
		side2Done = False
		side1 = []
		side2 = []
		while True:
			if (side1Done == False):
				side1Distance = side1Distance + SHAPE[i]['distance']
				if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 1
				if (i == maxPoint): side1Done = True
				side1.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )
			if (side1Done == True):
				side2Distance = side2Distance + SHAPE[i]['distance']
				if(i != minPoint and i != maxPoint): SHAPE[i]['side'] = 2
				if (i == minPoint): side2Done = True
				side2.append( { 'x': SHAPE[i]['x'], 'y': SHAPE[i]['y'] } )

			i = i + 1
			if( i > len(SHAPE)-1): i = 0

			if (side1Done and side2Done): break

			k = k + 1
			if (k == 20): break

		svg = '<?xml version="1.0" encoding="utf-8"?>'
		svg = svg + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
		svg = svg + '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-10 -10 120 120" xml:space="preserve">'

                # background
		svg = svg + '<circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="' + BG_COLOR + '"/>'

		minPointShadowX = SHAPE[minPoint]['x'] + WIDTH * math.cos(math.radians(minAngle))
		minPointShadowY = SHAPE[minPoint]['y'] - HEIGHT * math.sin(math.radians(minAngle))
		maxPointShadowX = SHAPE[maxPoint]['x'] + WIDTH * math.cos(math.radians(maxAngle))
		maxPointShadowY = SHAPE[maxPoint]['y'] - HEIGHT * math.sin(math.radians(maxAngle))

		shadow = [ {'x': maxPointShadowX, 'y': maxPointShadowY } ] + \
				side2 + \
				[ {'x': minPointShadowX, 'y': minPointShadowY } ]
		
		svg = svg + '<defs><mask id="shadowMask">'
		svg = svg + '	  <rect width="100%" height="100%" fill="black"/>'
		svg = svg + '	  <circle cx="' + str(WIDTH/2) + '" cy="' + str(HEIGHT/2) + '" r="' + str(WIDTH/2-1) + '" fill="white"/>'
		svg = svg + '</mask></defs>'

		svg = svg + self.generatePath('none',PRIMARY_COLOR,SHAPE)

		shadow_svg = self.generatePath('none','black',shadow,'mask="url(#shadowMask)" fill-opacity="0.5"')

		if (self.elevation>0): 
			svg = svg + self.generatePath(LIGHT_COLOR,'none',side2)
		else:
			svg = svg + self.generatePath(PRIMARY_COLOR,'none',side2)

		if (self.elevation>0): svg = svg + shadow_svg

		svg = svg + self.generateArc(WIDTH/2,PRIMARY_COLOR,'none',self.sunset_azimuth,self.sunrise_azimuth)
		svg = svg + self.generateArc(WIDTH/2,LIGHT_COLOR,'none',self.sunrise_azimuth,self.sunset_azimuth)

		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunrise_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunrise_azimuth,WIDTH/2+2)])
		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(self.sunset_azimuth,WIDTH/2-2), self.degreesToPoint(self.sunset_azimuth,WIDTH/2+2)])

		for i in range(0,len(DEGS)):
			if (i == len(DEGS)-1):
				j = 0
			else:
				j = i + 1
			if (i % 2 == 0):
				svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,'none',DEGS[i],DEGS[j],'stroke-width="3" stroke-opacity="0.2"')	
			else:
				svg = svg + self.generateArc(WIDTH/2+8,PRIMARY_COLOR,'none',DEGS[i],DEGS[j],'stroke-width="3" ')

		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[0],WIDTH/2+5), self.degreesToPoint(DEGS[0],WIDTH/2+11)])
		svg = svg + self.generatePath(LIGHT_COLOR,'none',[self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+5), self.degreesToPoint(DEGS[(len(DEGS))/2],WIDTH/2+11)])

		# moon drawing: compute left and right arcs
		phase = self.astr.moon_phase(self.now)
		if self.debug:
			print('phase: ' + str(phase))
		left_radius=MOON_RADIUS
		left_sweep=0
		right_radius=MOON_RADIUS
		right_sweep=0
		if (phase > 14):
			right_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
			if (right_radius < 0):
				right_radius = right_radius * -1.0
				right_sweep = 0
			else:
				right_sweep = 1
		
		if (phase < 14):
			left_radius = MOON_RADIUS - (2.0*MOON_RADIUS* (1.0 - ((phase%14)*0.99 / 14.0)))
			if (left_radius < 0):
				left_radius = left_radius * -1.0
				left_sweep = 1
				
		if (self.moon_elevation>0): 
			svg = svg + '<path stroke="none" stroke-width="0" fill="' + MOON_COLOR \
			+ '" d="M ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) \
			+ ' A ' + str(left_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(left_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']+MOON_RADIUS) \
			+ '   ' + str(right_radius) + ' ' + str(MOON_RADIUS) + ' 0 0 ' + str(right_sweep) + ' ' + str(moon_pos['x']) + ' ' + str(moon_pos['y']-MOON_RADIUS) + ' z" />'

		# sun drawing
		if (self.sun_elevation>0): 
			svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '55" />'
			svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -1) + '" stroke="none" stroke-width="0" fill="' + SUN_COLOR + '99" />'
			svg = svg + '<circle cx="' + str(sun_pos['x']) + '" cy="' + str(sun_pos['y']) + '" r="' + str(SUN_RADIUS -2) + '" stroke="' + SUN_COLOR + '" stroke-width="0" fill="' + SUN_COLOR + '" />'

		svg = svg + '</svg>'

		if self.debug:
			print(svg)

		f = open(FILENAME, 'w')
		f.write(svg)
		f.close()


def main():

	t1 = datetime.now()

	s = shadow()

	args = sys.argv
		
	if(len(args) == 1):
		dummy = 0
		#print('\033[91mNo parameters specified\033[0;0m')
	else:
		if(args[1] == "update"):
			s.generateSVG()

	t2 = datetime.now()
	print("Done in " + str(t2-t1) + " seconds")

if __name__ == '__main__':
	main()

2 Likes

Hi,

Thanks for the update-work!! Also to @vzorglub!

One question though: In the original script, the illuminated part of the house’s fassade would be highlighted in the SVG.
With your code and on my setup, the part where the shadow is cast on is actually highlighted (the part of the house not faced by the sun).
Is there a way to update/modify the script in order to have the side of the house, touched by sunlight being highlighted?

Kurt

Yes, there is a slight bug!!
Look for that bit and change the first one to side1

		if (self.elevation>0): 
			svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
		else:
			svg = svg + self.generatePath(PRIMARY_COLOR,'none',side2)

I inverted side1 and side2 in this condition as my svg output had the opposite side highlighted! With the correction purpose by @vzorglub I got the attached result. Any idea?

shadow

I had to tinker with your code to make it work as I wanted. I didn’t like your background, sorry!!

I use this snippet:
My test is sun_elevation because I don’t want an illuminated side for the moon!!

		if (self.sun_elevation > 0): 
			svg = svg + self.generatePath(LIGHT_COLOR,'none',side1)
		else:
			svg = svg + self.generatePath(PRIMARY_COLOR,'none',side2)

That works for me

image

Gotcha! This is linked to the clockwise/counter clockwise points in SHAPE used to draw the path! It must be in clockwise to work.

Yep, agree there, I drew mine clockwise. Well spotted young man

Yes, I understand! You can just set BG_COLOR to ‘none’ to cancel the background.
In my case I want to see the moon shadows :star_struck:!

I just commented out the line…

Super @vzorglub and @rotulet!
Thanks to both of you for the fast fix!

Kurt

Dumb question.

Do I need to do anything special to run the python scripts? If I put them in the scripts folder, is that sufficient or do they need to be executed?

See post #1

I’ve now got it running using your code, vzorglub and all looking good, except the actual graphics don’t update in HabPanel. Only if I delete the widget and add it again will it update to show the correct sun position.

I’m using this in the widget template:

<object data="/static/matrix-theme/shaddow.svg?{{itemValue('Sun_Elevation')}}" type="image/svg+xml"></object>      

Logs show that Sun_Elevation and Sun_Azimuth are all updating every 300 seconds. Any ideas why it won’t update?

Are you sure that your svg file is saved in /html/matrix-theme ?
If yes, then I have no idea. Not good with html and web dev.