Make it possible to generate an SVG image
This commit is contained in:
parent
7d15bd1956
commit
95d7d89470
@ -81,3 +81,12 @@ It can theoretically make it easier to synchronize events across multiple time
|
|||||||
zones without actually knowing the time difference; just tell the other
|
zones without actually knowing the time difference; just tell the other
|
||||||
attending parties that you eat your lunch during Ladybug hour, or that your
|
attending parties that you eat your lunch during Ladybug hour, or that your
|
||||||
event is taking place between Gourd and Soup hours.
|
event is taking place between Gourd and Soup hours.
|
||||||
|
|
||||||
|
Generated SVG
|
||||||
|
=============
|
||||||
|
|
||||||
|
With the ``poetry run print_svg`` command, you will get an image similar to this:
|
||||||
|
|
||||||
|
.. image:: example-output.svg
|
||||||
|
:width: 700
|
||||||
|
:alt: An image rendered by this software
|
||||||
|
681
example-output.svg
Normal file
681
example-output.svg
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
<svg width="700" height="700" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<style>
|
||||||
|
.hour path {stroke: rgb(0, 0, 0); stroke-width: 2px;}
|
||||||
|
.hour text {stroke: none; fill: rgb(238, 187, 85);}
|
||||||
|
.hour text.utc {stroke: none; fill: rgb(91, 68, 38);}
|
||||||
|
.winter path {fill: rgb(70, 62, 108);}
|
||||||
|
.spring path {fill: rgb(55, 87, 55);}
|
||||||
|
.summer path {fill: rgb(113, 92, 43);}
|
||||||
|
.autumn path {fill: rgb(108, 68, 44);}
|
||||||
|
.local-hour {stroke: none; fill: rgb(238, 187, 85);}
|
||||||
|
.night-time {stroke: none; fill: rgb(19, 17, 30);}
|
||||||
|
.blue-hour {stroke: none; fill: rgb(9, 1, 119);}
|
||||||
|
.golden-hour {stroke: none; fill: rgb(170, 132, 44);}
|
||||||
|
.day-time {stroke: none; fill: rgb(125, 197, 240);}
|
||||||
|
.marker {stroke: rgb(19, 17, 30); stroke-width: 2px; fill: none;}
|
||||||
|
.moon-background {stroke: rgb(170, 170, 170); stroke-width: 2px; fill: rgb(19, 17, 30);}
|
||||||
|
.moon {stroke: none; fill: rgb(170, 170, 170);}
|
||||||
|
.sun {stroke: none; fill: rgb(238, 187, 85);}
|
||||||
|
.dial {stroke-width: 2px; stroke: rgb(238, 187, 85);}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<path id="hour-name-path" d="M 312.1730467792776 62.6757645018362 a 289.803545 289.803545 15 0 1 75.65390644144473 0"></path>
|
||||||
|
</defs>
|
||||||
|
<rect x="0" y="0" width="700" height="700" style="stroke: none; fill: rgb(19, 17, 30);"></rect>
|
||||||
|
<g id="local-clock">
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(0, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">Midnight</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(15, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">1</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(30, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">2</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(45, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">3</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(60, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">4</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(75, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">5</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(90, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">6</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(105, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">7</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(120, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">8</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(135, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">9</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(150, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">10</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(165, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">11</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(180, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">Noon</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(195, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">13</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(210, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">14</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(225, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">15</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(240, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">16</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(255, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">17</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(270, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">18</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(285, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">19</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(300, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">20</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(315, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">21</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(330, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">22</text>
|
||||||
|
<text
|
||||||
|
transform="rotate(180, 350.0, 350.0) rotate(345, 350.0, 350.0)"
|
||||||
|
class="local-hour"
|
||||||
|
x="350.0"
|
||||||
|
y="31.88096999999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="16.5">23</text>
|
||||||
|
</g>
|
||||||
|
<g id="seasonal-clock" transform="rotate(30.0, 350.0, 350.0)">
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(0, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Candle</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 00</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(15, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Ice</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 01</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(30, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Comet</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 02</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(45, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Thimble</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 03</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(60, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Root</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 04</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour winter" transform="rotate(-172.5, 350.0, 350.0) rotate(75, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Mist</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 05</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(90, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Sprout</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 06</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(105, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Rainbow</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 07</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(120, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Worm</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 08</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(135, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Bud</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 09</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(150, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Blossom</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 10</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour spring" transform="rotate(-172.5, 350.0, 350.0) rotate(165, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Ladybug</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 11</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(180, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Geese</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 12</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(195, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Dust</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 13</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(210, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Peach</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 14</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(225, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Fog</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 15</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(240, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Acorn</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 16</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour summer" transform="rotate(-172.5, 350.0, 350.0) rotate(255, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Gourd</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 17</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(270, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Soup</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 18</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(285, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Crow</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 19</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(300, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Mushroom</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 20</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(315, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Thunder</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 21</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(330, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Frost</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 22</text>
|
||||||
|
</g>
|
||||||
|
<g class="hour autumn" transform="rotate(-172.5, 350.0, 350.0) rotate(345, 350.0, 350.0)">
|
||||||
|
<path
|
||||||
|
d="M 309.55397542717907 42.7819425076129 a 309.86903 309.86903 15 0 1 80.89204914564186 0 l -5.238142704197123 39.78764398844654 a 269.73806 269.73806 15 0 0 -70.41576373724762 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="350.0"
|
||||||
|
y="63.54070249999999"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="13.37699"><textPath xlink:href="#hour-name-path" startOffset="50%">Lantern</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, 350.0, 350.0)"
|
||||||
|
class="utc"
|
||||||
|
x="350.0"
|
||||||
|
y="95.28533999999998"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="15.0234">U 23</text>
|
||||||
|
</g>
|
||||||
|
<circle cx="350.0" cy="350.0" r="239.69126000000003" class="day-time"></circle>
|
||||||
|
<path
|
||||||
|
class="golden-hour"
|
||||||
|
d="M 350.0 350.0
|
||||||
|
L 116.05469504530768 402.16794427657305
|
||||||
|
A 239.69126000000003 239.69126000000003 169.29166666666669 1 0 570.178029285408 255.27125283008513
|
||||||
|
z"></path>
|
||||||
|
<circle cx="350.0" cy="514.934515" r="10" class="sun" transform="rotate(208.61666666666667, 350.0, 350.0)"></circle>
|
||||||
|
<path
|
||||||
|
class="blue-hour"
|
||||||
|
d="M 350.0 350.0
|
||||||
|
L 137.1262622524969 460.16656433689354
|
||||||
|
A 239.69126000000003 239.69126000000003 198.91666666666669 0 0 587.0918462571672 314.795503713937
|
||||||
|
z"></path>
|
||||||
|
<path
|
||||||
|
class="night-time"
|
||||||
|
d="M 350.0 350.0
|
||||||
|
L 143.12202233852076 471.0512390646379
|
||||||
|
A 239.69126000000003 239.69126000000003 204.86666666666665 0 0 588.60109398448 327.1654194302391
|
||||||
|
z"></path>
|
||||||
|
<circle cx="350.0" cy="350.0" r="239.69126000000003" class="marker"></circle>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<circle cx="350.0" cy="350.0" r="50" class="moon-background"></circle>
|
||||||
|
<path
|
||||||
|
class="moon"
|
||||||
|
d="M 350.0 300.0
|
||||||
|
C 283.0 306.0, 283.0 394.0, 350.0 400.0
|
||||||
|
C 318.4132222222222 394.0, 318.4132222222222 306.0, 350.0 300.0"></path>
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
style="stroke: red;"
|
||||||
|
transform="rotate(341.90416666666664, 350.0, 350.0)"
|
||||||
|
x1="350.0"
|
||||||
|
y1="579.69126"
|
||||||
|
x2="350.0"
|
||||||
|
y2="589.69126"></line>
|
||||||
|
<line
|
||||||
|
style="stroke: red;"
|
||||||
|
transform="rotate(162.00833333333333, 350.0, 350.0)"
|
||||||
|
x1="350.0"
|
||||||
|
y1="579.69126"
|
||||||
|
x2="350.0"
|
||||||
|
y2="589.69126"></line>
|
||||||
|
<line
|
||||||
|
id="dial"
|
||||||
|
transform="rotate(238.61666666666667, 350.0, 350.0)"
|
||||||
|
class="dial"
|
||||||
|
x1="350.0"
|
||||||
|
y1="504.93451500000003"
|
||||||
|
x2="350.0"
|
||||||
|
y2="633.11505"></line>
|
||||||
|
</svg>
|
||||||
|
|
After Width: | Height: | Size: 31 KiB |
@ -23,6 +23,7 @@ types-pytz = "^2021.3.6"
|
|||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
print_data = "seasonal_clock:main"
|
print_data = "seasonal_clock:main"
|
||||||
|
print_svg = "seasonal_clock.svg:print_svg"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
441
seasonal_clock/svg.py
Normal file
441
seasonal_clock/svg.py
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
from datetime import datetime, time
|
||||||
|
from math import ceil, cos, pi, radians, sin
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from astral import LocationInfo, moon
|
||||||
|
from pytz import UTC, timezone
|
||||||
|
|
||||||
|
from .config import load_config
|
||||||
|
from .times import collect_day_parts
|
||||||
|
|
||||||
|
HOURS_AMPM = (
|
||||||
|
'Midnight',
|
||||||
|
'1a',
|
||||||
|
'2a',
|
||||||
|
'3a',
|
||||||
|
'4a',
|
||||||
|
'5a',
|
||||||
|
'6a',
|
||||||
|
'7a',
|
||||||
|
'8a',
|
||||||
|
'9a',
|
||||||
|
'10a',
|
||||||
|
'11a',
|
||||||
|
'Noon',
|
||||||
|
'1p',
|
||||||
|
'2p',
|
||||||
|
'3p',
|
||||||
|
'4p',
|
||||||
|
'5p',
|
||||||
|
'6p',
|
||||||
|
'7p',
|
||||||
|
'8p',
|
||||||
|
'9p',
|
||||||
|
'10p',
|
||||||
|
'11p',
|
||||||
|
)
|
||||||
|
HOURS_24 = (
|
||||||
|
'Midnight',
|
||||||
|
'1',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4',
|
||||||
|
'5',
|
||||||
|
'6',
|
||||||
|
'7',
|
||||||
|
'8',
|
||||||
|
'9',
|
||||||
|
'10',
|
||||||
|
'11',
|
||||||
|
'Noon',
|
||||||
|
'13',
|
||||||
|
'14',
|
||||||
|
'15',
|
||||||
|
'16',
|
||||||
|
'17',
|
||||||
|
'18',
|
||||||
|
'19',
|
||||||
|
'20',
|
||||||
|
'21',
|
||||||
|
'22',
|
||||||
|
'23',
|
||||||
|
)
|
||||||
|
HOUR_NAMES = (
|
||||||
|
'Candle',
|
||||||
|
'Ice',
|
||||||
|
'Comet',
|
||||||
|
'Thimble',
|
||||||
|
'Root',
|
||||||
|
'Mist',
|
||||||
|
'Sprout',
|
||||||
|
'Rainbow',
|
||||||
|
'Worm',
|
||||||
|
'Bud',
|
||||||
|
'Blossom',
|
||||||
|
'Ladybug',
|
||||||
|
'Geese',
|
||||||
|
'Dust',
|
||||||
|
'Peach',
|
||||||
|
'Fog',
|
||||||
|
'Acorn',
|
||||||
|
'Gourd',
|
||||||
|
'Soup',
|
||||||
|
'Crow',
|
||||||
|
'Mushroom',
|
||||||
|
'Thunder',
|
||||||
|
'Frost',
|
||||||
|
'Lantern',
|
||||||
|
)
|
||||||
|
SEASONS = ('winter', 'spring', 'summer', 'autumn')
|
||||||
|
|
||||||
|
|
||||||
|
def hex_to_rgb(hex_color: str) -> str:
|
||||||
|
r = int(hex_color[1:3], 16)
|
||||||
|
g = int(hex_color[3:5], 16)
|
||||||
|
b = int(hex_color[5:7], 16)
|
||||||
|
|
||||||
|
return f'rgb({r}, {g}, {b})'
|
||||||
|
|
||||||
|
|
||||||
|
def get_utc_offset(utc_time: datetime, local_time: datetime):
|
||||||
|
assert utc_time.tzinfo
|
||||||
|
assert local_time.tzinfo
|
||||||
|
|
||||||
|
utc = utc_time.replace(tzinfo=None)
|
||||||
|
local = utc_time.replace(tzinfo=None)
|
||||||
|
|
||||||
|
return (utc - local).total_seconds()
|
||||||
|
|
||||||
|
|
||||||
|
def seconds_to_degrees(seconds: float) -> float:
|
||||||
|
one_second = 360 / 86400
|
||||||
|
|
||||||
|
return seconds * one_second
|
||||||
|
|
||||||
|
|
||||||
|
def indent_lines(string: str, indentation: int = 8) -> str:
|
||||||
|
return '\n'.join((' ' * indentation) + line for line in string.split('\n')) + '\n'
|
||||||
|
|
||||||
|
|
||||||
|
def time_to_degrees(timestamp: Union[datetime, time]) -> float:
|
||||||
|
return seconds_to_degrees(
|
||||||
|
timestamp.hour * 3600 + timestamp.minute * 60 + timestamp.second
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def hour_name_path(image_width: int, outer_r: float, ring_width: float) -> str:
|
||||||
|
radius = outer_r - ring_width / 2
|
||||||
|
delta_x = radius * sin(radians(15) / 2)
|
||||||
|
delta_y = radius * (1 - cos(radians(15) / 2))
|
||||||
|
x1 = image_width / 2 - delta_x
|
||||||
|
y1 = (image_width / 2 - radius) + delta_y
|
||||||
|
|
||||||
|
return f'''<path id="hour-name-path" d="M {x1} {y1} a {radius} {radius} 15 0 1 {2 * delta_x} 0"></path>'''
|
||||||
|
|
||||||
|
|
||||||
|
def hour_marker(
|
||||||
|
hour: int,
|
||||||
|
hour_name: str,
|
||||||
|
image_width: int,
|
||||||
|
outer_r: float,
|
||||||
|
ring_width: float,
|
||||||
|
hour_name_font_size: float,
|
||||||
|
utc_font_size: float,
|
||||||
|
indent: int = 8,
|
||||||
|
) -> str:
|
||||||
|
season = SEASONS[ceil((hour + 1) / 6) - 1]
|
||||||
|
rotation = hour * 15
|
||||||
|
|
||||||
|
delta_x = outer_r * sin(radians(15) / 2)
|
||||||
|
delta_y = outer_r * (1 - cos(radians(15) / 2))
|
||||||
|
|
||||||
|
s_delta_x = 0 - ring_width * sin(radians(15) / 2)
|
||||||
|
s_delta_y = ring_width * cos(radians(15) / 2)
|
||||||
|
|
||||||
|
i_delta_x = -2 * (outer_r - ring_width) * sin(radians(15) / 2)
|
||||||
|
|
||||||
|
x1 = image_width / 2 - delta_x
|
||||||
|
y1 = (image_width / 2 - outer_r) + delta_y
|
||||||
|
|
||||||
|
hour_name_y = image_width / 2 - outer_r + ring_width / 2 + hour_name_font_size / 4
|
||||||
|
utc_hour_y = image_width / 2 - outer_r + ring_width + utc_font_size
|
||||||
|
|
||||||
|
ret = f'''<g class="hour {season}" transform="rotate(-172.5, {image_width / 2}, {image_width / 2}) rotate({rotation}, {image_width / 2}, {image_width / 2})">
|
||||||
|
<path
|
||||||
|
d="M {x1} {y1} a {outer_r} {outer_r} 15 0 1 {2 * delta_x} 0 l {s_delta_x} {s_delta_y} a {outer_r - ring_width} {outer_r - ring_width} 15 0 0 {i_delta_x} 0 z"></path>
|
||||||
|
<text
|
||||||
|
x="{image_width / 2}"
|
||||||
|
y="{hour_name_y}"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="{hour_name_font_size}"><textPath xlink:href="#hour-name-path" startOffset="50%">{hour_name}</textPath></text>
|
||||||
|
<text
|
||||||
|
transform="rotate(-7.5, {image_width / 2}, {image_width / 2})"
|
||||||
|
class="utc"
|
||||||
|
x="{image_width / 2}"
|
||||||
|
y="{utc_hour_y}"
|
||||||
|
text-anchor="middle"
|
||||||
|
dominant-baseline="mathematical"
|
||||||
|
font-size="{utc_font_size}">U {hour:02d}</text>
|
||||||
|
</g>'''
|
||||||
|
|
||||||
|
return indent_lines(ret, indent)
|
||||||
|
|
||||||
|
|
||||||
|
def local_hour(
|
||||||
|
image_width: int,
|
||||||
|
outer_r: float,
|
||||||
|
hour_num: int,
|
||||||
|
hour: str,
|
||||||
|
font_size: float,
|
||||||
|
indent: int = 8,
|
||||||
|
) -> str:
|
||||||
|
rotation = hour_num * 15
|
||||||
|
|
||||||
|
return indent_lines(
|
||||||
|
f'''<text
|
||||||
|
transform="rotate(180, {image_width / 2}, {image_width / 2}) rotate({rotation}, {image_width / 2}, {image_width / 2})"
|
||||||
|
class="local-hour"
|
||||||
|
x="{image_width / 2}"
|
||||||
|
y="{image_width / 2 - outer_r - font_size / 2}"
|
||||||
|
text-anchor="middle"
|
||||||
|
font-size="{font_size}">{hour}</text>''',
|
||||||
|
indent,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_range_path(
|
||||||
|
image_width: int,
|
||||||
|
radius: float,
|
||||||
|
range_name: str,
|
||||||
|
start_time: time,
|
||||||
|
end_time: time,
|
||||||
|
outer: bool = True,
|
||||||
|
indent: int = 8,
|
||||||
|
) -> str:
|
||||||
|
start_deg = time_to_degrees(start_time)
|
||||||
|
end_deg = time_to_degrees(end_time)
|
||||||
|
|
||||||
|
start_delta_x = radius * sin(radians(start_deg))
|
||||||
|
start_delta_y = radius * (1 - cos(radians(start_deg)))
|
||||||
|
end_delta_x = radius * sin(radians(end_deg))
|
||||||
|
end_delta_y = radius * (1 - cos(radians(end_deg)))
|
||||||
|
|
||||||
|
deg_diff = end_deg - start_deg
|
||||||
|
|
||||||
|
large_arc_flag = 0 if abs(deg_diff) >= 180 else 1
|
||||||
|
|
||||||
|
return indent_lines(
|
||||||
|
f'''<path
|
||||||
|
class="{range_name}"
|
||||||
|
d="M {image_width / 2} {image_width / 2}
|
||||||
|
L {image_width / 2 - start_delta_x} {image_width / 2 + radius - start_delta_y}
|
||||||
|
A {radius} {radius} {end_deg - start_deg} {large_arc_flag} 0 {image_width / 2 - end_delta_x} {image_width / 2 + radius - end_delta_y}
|
||||||
|
z"></path>''',
|
||||||
|
indent,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_moon_path(image_width: int, radius: float, moon_phase: float, indent: int = 8):
|
||||||
|
handle_x_pos = radius * 1.34
|
||||||
|
handle_y_pos = radius * 0.88
|
||||||
|
min_x = image_width / 2 - handle_x_pos
|
||||||
|
max_x = min_x + 2 * handle_x_pos
|
||||||
|
top_y = image_width / 2 - handle_y_pos
|
||||||
|
bottom_y = image_width / 2 + handle_y_pos
|
||||||
|
|
||||||
|
if moon_phase < 14:
|
||||||
|
h1_x = min_x + 2 * handle_x_pos * (1 - moon_phase / 14)
|
||||||
|
h2_x = max_x
|
||||||
|
else:
|
||||||
|
h1_x = min_x
|
||||||
|
h2_x = max_x + 2 * handle_x_pos * (1 - moon_phase / 14)
|
||||||
|
|
||||||
|
ret = f'''<path
|
||||||
|
class="moon"
|
||||||
|
d="M {image_width / 2} {image_width / 2 - radius}
|
||||||
|
C {h1_x} {top_y}, {h1_x} {bottom_y}, {image_width / 2} {image_width / 2 + radius}
|
||||||
|
C {h2_x} {bottom_y}, {h2_x} {top_y}, {image_width / 2} {image_width / 2 - radius}"></path>'''
|
||||||
|
|
||||||
|
return indent_lines(ret, indent)
|
||||||
|
|
||||||
|
|
||||||
|
def get_svg_data(
|
||||||
|
local_time: datetime,
|
||||||
|
image_width: int = 700,
|
||||||
|
line_width: str = '2px',
|
||||||
|
line_color: str = '#eebb55',
|
||||||
|
utc_color: str = '#5b4426',
|
||||||
|
hname_stroke_color: str = '#000000',
|
||||||
|
winter_color: str = '#463e6c',
|
||||||
|
spring_color: str = '#375737',
|
||||||
|
summer_color: str = '#715c2b',
|
||||||
|
autumn_color: str = '#6c442c',
|
||||||
|
night_color: str = '#13111e',
|
||||||
|
blue_color: str = '#090177',
|
||||||
|
golden_color: str = '#aa842c',
|
||||||
|
day_color: str = '#7dc5f0',
|
||||||
|
moon_color: str = '#aaaaaa',
|
||||||
|
local_hour_font_size: float = 16.5,
|
||||||
|
hour_name_font_size: float = 13.37699,
|
||||||
|
utc_hour_font_size: float = 15.0234,
|
||||||
|
hour_24: bool = True,
|
||||||
|
) -> str:
|
||||||
|
line_rgb = hex_to_rgb(line_color)
|
||||||
|
hname_stroke_rgb = hex_to_rgb(hname_stroke_color)
|
||||||
|
utc_rgb = hex_to_rgb(utc_color)
|
||||||
|
winter_rgb = hex_to_rgb(winter_color)
|
||||||
|
spring_rgb = hex_to_rgb(spring_color)
|
||||||
|
summer_rgb = hex_to_rgb(summer_color)
|
||||||
|
autumn_rgb = hex_to_rgb(autumn_color)
|
||||||
|
night_rgb = hex_to_rgb(night_color)
|
||||||
|
blue_rgb = hex_to_rgb(blue_color)
|
||||||
|
golden_rgb = hex_to_rgb(golden_color)
|
||||||
|
day_rgb = hex_to_rgb(day_color)
|
||||||
|
moon_rgb = hex_to_rgb(moon_color)
|
||||||
|
hours = HOURS_24 if hour_24 else HOURS_AMPM
|
||||||
|
outer_r = image_width / 2 - 3 * hour_name_font_size
|
||||||
|
ring_width = hour_name_font_size * 3
|
||||||
|
|
||||||
|
utc_time = local_time.astimezone(UTC)
|
||||||
|
utc_naive = utc_time.replace(tzinfo=None)
|
||||||
|
local_naive = local_time.replace(tzinfo=None)
|
||||||
|
offset = (local_naive - utc_naive).total_seconds()
|
||||||
|
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
location = LocationInfo(
|
||||||
|
config['city'],
|
||||||
|
config['country'],
|
||||||
|
config['timezone'],
|
||||||
|
config['latitude'],
|
||||||
|
config['longitude'],
|
||||||
|
)
|
||||||
|
local_tz = location.tzinfo
|
||||||
|
day_parts = collect_day_parts(location.observer, local_time)
|
||||||
|
day_parts_dict = dict(part[0:2] for part in day_parts)
|
||||||
|
morning_blue_start = day_parts_dict.pop('morning_blue_start')
|
||||||
|
morning_blue_end = day_parts_dict.pop('morning_golden_start')
|
||||||
|
morning_golden_end = day_parts_dict.pop('morning_golden_end')
|
||||||
|
evening_golden_start = day_parts_dict.pop('evening_golden_start')
|
||||||
|
evening_blue_start = day_parts_dict.pop('evening_blue_start')
|
||||||
|
evening_blue_end = day_parts_dict.pop('evening_blue_end')
|
||||||
|
moon_phase = moon.phase(local_time)
|
||||||
|
|
||||||
|
noon = day_parts_dict.pop('noon')
|
||||||
|
midnight = day_parts_dict.pop('midnight')
|
||||||
|
|
||||||
|
ret = f'''<svg width="{image_width}" height="{image_width}" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<style>
|
||||||
|
.hour path {{stroke: {hname_stroke_rgb}; stroke-width: {line_width};}}
|
||||||
|
.hour text {{stroke: none; fill: {line_rgb};}}
|
||||||
|
.hour text.utc {{stroke: none; fill: {utc_rgb};}}
|
||||||
|
.winter path {{fill: {winter_rgb};}}
|
||||||
|
.spring path {{fill: {spring_rgb};}}
|
||||||
|
.summer path {{fill: {summer_rgb};}}
|
||||||
|
.autumn path {{fill: {autumn_rgb};}}
|
||||||
|
.local-hour {{stroke: none; fill: {line_rgb};}}
|
||||||
|
.night-time {{stroke: none; fill: {night_rgb};}}
|
||||||
|
.blue-hour {{stroke: none; fill: {blue_rgb};}}
|
||||||
|
.golden-hour {{stroke: none; fill: {golden_rgb};}}
|
||||||
|
.day-time {{stroke: none; fill: {day_rgb};}}
|
||||||
|
.marker {{stroke: {night_rgb}; stroke-width: 2px; fill: none;}}
|
||||||
|
.moon-background {{stroke: {moon_rgb}; stroke-width: 2px; fill: {night_rgb};}}
|
||||||
|
.moon {{stroke: none; fill: {moon_rgb};}}
|
||||||
|
.sun {{stroke: none; fill: {line_rgb};}}
|
||||||
|
.dial {{stroke-width: 2px; stroke: {line_rgb};}}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
{hour_name_path(image_width, outer_r, ring_width)}
|
||||||
|
</defs>
|
||||||
|
<rect x="0" y="0" width="{image_width}" height="{image_width}" style="stroke: none; fill: {night_rgb};"></rect>
|
||||||
|
<g id="local-clock">\n'''
|
||||||
|
|
||||||
|
for hour in range(24):
|
||||||
|
ret += local_hour(
|
||||||
|
image_width, outer_r, hour, hours[hour], local_hour_font_size, indent=8
|
||||||
|
)
|
||||||
|
|
||||||
|
ret += f''' </g>
|
||||||
|
<g id="seasonal-clock" transform="rotate({seconds_to_degrees(offset)}, {image_width / 2}, {image_width / 2})">
|
||||||
|
'''
|
||||||
|
for hour in range(24):
|
||||||
|
ret += hour_marker(
|
||||||
|
hour,
|
||||||
|
HOUR_NAMES[hour],
|
||||||
|
image_width,
|
||||||
|
outer_r,
|
||||||
|
ring_width,
|
||||||
|
hour_name_font_size,
|
||||||
|
utc_hour_font_size,
|
||||||
|
indent=8,
|
||||||
|
)
|
||||||
|
|
||||||
|
marker_radius = outer_r - ring_width - 2 * utc_hour_font_size
|
||||||
|
ret += f''' <circle cx="{image_width / 2}" cy="{image_width / 2}" r="{marker_radius}" class="day-time"></circle>\n'''
|
||||||
|
ret += get_range_path(
|
||||||
|
image_width,
|
||||||
|
marker_radius,
|
||||||
|
'golden-hour',
|
||||||
|
morning_golden_end.time(),
|
||||||
|
evening_golden_start.time(),
|
||||||
|
)
|
||||||
|
sun_radius = 10
|
||||||
|
ret += f''' <circle cx="{image_width / 2}" cy="{image_width / 2 + outer_r / 2 + sun_radius}" r="{sun_radius}" class="sun" transform="rotate({time_to_degrees(local_time.astimezone(UTC))}, {image_width / 2}, {image_width / 2})"></circle>\n'''
|
||||||
|
ret += get_range_path(
|
||||||
|
image_width,
|
||||||
|
marker_radius,
|
||||||
|
'blue-hour',
|
||||||
|
morning_blue_end.time(),
|
||||||
|
evening_blue_start.time(),
|
||||||
|
)
|
||||||
|
ret += get_range_path(
|
||||||
|
image_width,
|
||||||
|
marker_radius,
|
||||||
|
'night-time',
|
||||||
|
morning_blue_start.time(),
|
||||||
|
evening_blue_end.time(),
|
||||||
|
)
|
||||||
|
|
||||||
|
ret += f''' <circle cx="{image_width / 2}" cy="{image_width / 2}" r="{marker_radius}" class="marker"></circle>
|
||||||
|
</g>
|
||||||
|
<g>\n'''
|
||||||
|
|
||||||
|
moon_radius = 50
|
||||||
|
ret += f''' <circle cx="{image_width / 2}" cy="{image_width / 2}" r="{moon_radius}" class="moon-background"></circle>\n'''
|
||||||
|
|
||||||
|
ret += get_moon_path(image_width, moon_radius, moon_phase, indent=8)
|
||||||
|
ret += f''' </g>
|
||||||
|
<line
|
||||||
|
style="stroke: red;"
|
||||||
|
transform="rotate({time_to_degrees(midnight.astimezone(local_tz))}, {image_width / 2}, {image_width / 2})"
|
||||||
|
x1="{image_width / 2}"
|
||||||
|
y1="{image_width / 2 + marker_radius - sun_radius}"
|
||||||
|
x2="{image_width / 2}"
|
||||||
|
y2="{image_width / 2 + marker_radius}"></line>
|
||||||
|
<line
|
||||||
|
style="stroke: red;"
|
||||||
|
transform="rotate({time_to_degrees(noon.astimezone(local_tz))}, {image_width / 2}, {image_width / 2})"
|
||||||
|
x1="{image_width / 2}"
|
||||||
|
y1="{image_width / 2 + marker_radius - sun_radius}"
|
||||||
|
x2="{image_width / 2}"
|
||||||
|
y2="{image_width / 2 + marker_radius}"></line>
|
||||||
|
<line
|
||||||
|
id="dial"
|
||||||
|
transform="rotate({time_to_degrees(local_time)}, {image_width / 2}, {image_width / 2})"
|
||||||
|
class="dial"
|
||||||
|
x1="{image_width / 2}"
|
||||||
|
y1="{image_width / 2 + outer_r * 0.5}"
|
||||||
|
x2="{image_width / 2}"
|
||||||
|
y2="{image_width / 2 + outer_r - ring_width + hour_name_font_size}"></line>
|
||||||
|
</svg>
|
||||||
|
'''
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def print_svg():
|
||||||
|
utc_now = datetime.utcnow().replace(tzinfo=UTC)
|
||||||
|
local_now = utc_now.astimezone(timezone('Europe/Budapest'))
|
||||||
|
|
||||||
|
print(get_svg_data(local_now))
|
Loading…
Reference in New Issue
Block a user