kerykeion 4.18.3__py3-none-any.whl → 5.1.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kerykeion might be problematic. Click here for more details.

Files changed (76) hide show
  1. kerykeion/__init__.py +56 -11
  2. kerykeion/aspects/__init__.py +7 -4
  3. kerykeion/aspects/aspects_factory.py +568 -0
  4. kerykeion/aspects/aspects_utils.py +86 -13
  5. kerykeion/astrological_subject_factory.py +1901 -0
  6. kerykeion/backword.py +820 -0
  7. kerykeion/chart_data_factory.py +552 -0
  8. kerykeion/charts/__init__.py +2 -2
  9. kerykeion/charts/chart_drawer.py +2794 -0
  10. kerykeion/charts/charts_utils.py +1066 -309
  11. kerykeion/charts/draw_planets.py +602 -351
  12. kerykeion/charts/templates/aspect_grid_only.xml +337 -193
  13. kerykeion/charts/templates/chart.xml +441 -240
  14. kerykeion/charts/templates/wheel_only.xml +365 -211
  15. kerykeion/charts/themes/black-and-white.css +148 -0
  16. kerykeion/charts/themes/classic.css +107 -76
  17. kerykeion/charts/themes/dark-high-contrast.css +145 -107
  18. kerykeion/charts/themes/dark.css +146 -107
  19. kerykeion/charts/themes/light.css +146 -103
  20. kerykeion/charts/themes/strawberry.css +158 -0
  21. kerykeion/composite_subject_factory.py +408 -0
  22. kerykeion/ephemeris_data_factory.py +443 -0
  23. kerykeion/fetch_geonames.py +81 -21
  24. kerykeion/house_comparison/__init__.py +6 -0
  25. kerykeion/house_comparison/house_comparison_factory.py +103 -0
  26. kerykeion/house_comparison/house_comparison_utils.py +126 -0
  27. kerykeion/kr_types/__init__.py +66 -6
  28. kerykeion/kr_types/chart_template_model.py +20 -0
  29. kerykeion/kr_types/kerykeion_exception.py +15 -9
  30. kerykeion/kr_types/kr_literals.py +14 -106
  31. kerykeion/kr_types/kr_models.py +14 -179
  32. kerykeion/kr_types/settings_models.py +15 -152
  33. kerykeion/planetary_return_factory.py +805 -0
  34. kerykeion/relationship_score_factory.py +301 -0
  35. kerykeion/report.py +750 -65
  36. kerykeion/schemas/__init__.py +106 -0
  37. kerykeion/schemas/chart_template_model.py +367 -0
  38. kerykeion/schemas/kerykeion_exception.py +20 -0
  39. kerykeion/schemas/kr_literals.py +181 -0
  40. kerykeion/schemas/kr_models.py +603 -0
  41. kerykeion/schemas/settings_models.py +188 -0
  42. kerykeion/settings/__init__.py +20 -1
  43. kerykeion/settings/chart_defaults.py +444 -0
  44. kerykeion/settings/config_constants.py +152 -0
  45. kerykeion/settings/kerykeion_settings.py +36 -61
  46. kerykeion/settings/translation_strings.py +1499 -0
  47. kerykeion/settings/translations.py +74 -0
  48. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  49. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  50. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  51. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  52. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  53. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  54. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  55. kerykeion/sweph/sefstars.txt +1602 -0
  56. kerykeion/transits_time_range_factory.py +302 -0
  57. kerykeion/utilities.py +626 -125
  58. kerykeion-5.1.9.dist-info/METADATA +1793 -0
  59. kerykeion-5.1.9.dist-info/RECORD +63 -0
  60. {kerykeion-4.18.3.dist-info → kerykeion-5.1.9.dist-info}/WHEEL +1 -1
  61. kerykeion/aspects/natal_aspects.py +0 -143
  62. kerykeion/aspects/synastry_aspects.py +0 -113
  63. kerykeion/astrological_subject.py +0 -818
  64. kerykeion/charts/kerykeion_chart_svg.py +0 -894
  65. kerykeion/enums.py +0 -51
  66. kerykeion/ephemeris_data.py +0 -178
  67. kerykeion/kr_types/chart_types.py +0 -88
  68. kerykeion/relationship_score/__init__.py +0 -2
  69. kerykeion/relationship_score/relationship_score.py +0 -175
  70. kerykeion/relationship_score/relationship_score_factory.py +0 -275
  71. kerykeion/settings/kr.config.json +0 -721
  72. kerykeion-4.18.3.dist-info/LICENSE +0 -661
  73. kerykeion-4.18.3.dist-info/METADATA +0 -396
  74. kerykeion-4.18.3.dist-info/RECORD +0 -42
  75. kerykeion-4.18.3.dist-info/entry_points.txt +0 -3
  76. /LICENSE → /kerykeion-5.1.9.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,1793 @@
1
+ Metadata-Version: 2.4
2
+ Name: kerykeion
3
+ Version: 5.1.9
4
+ Summary: A Python library for astrological calculations, including natal charts, houses, planetary aspects, and SVG chart generation.
5
+ Project-URL: Homepage, https://www.kerykeion.net/
6
+ Project-URL: Repository, https://github.com/g-battaglia/kerykeion
7
+ Author-email: Giacomo Battaglia <kerykeion.astrology@gmail.com>
8
+ License: AGPL-3.0
9
+ License-File: LICENSE
10
+ Keywords: astrology,astrology calculations,astrology calculator,astrology library,astrology transits,astronomical algorithms,birth chart,ephemeris,houses of astrology,natal chart,planetary aspects,svg charts,synastry,zodiac,zodiac signs
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Religion
24
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
25
+ Classifier: Topic :: Scientific/Engineering :: Visualization
26
+ Classifier: Topic :: Software Development
27
+ Classifier: Topic :: Software Development :: Libraries
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.9
31
+ Requires-Dist: pydantic>=2.5
32
+ Requires-Dist: pyswisseph>=2.10.3.1
33
+ Requires-Dist: pytz>=2024.2
34
+ Requires-Dist: requests-cache>=1.2.1
35
+ Requires-Dist: requests>=2.32.3
36
+ Requires-Dist: scour>=0.38.2
37
+ Requires-Dist: simple-ascii-tables>=1.0.0
38
+ Requires-Dist: typing-extensions>=4.12.2
39
+ Description-Content-Type: text/markdown
40
+
41
+ <h1 align="center">Kerykeion</h1>
42
+
43
+ <div align="center">
44
+ <img src="https://img.shields.io/github/stars/g-battaglia/kerykeion.svg?logo=github" alt="stars">
45
+ <img src="https://img.shields.io/github/forks/g-battaglia/kerykeion.svg?logo=github" alt="forks">
46
+ </div>
47
+ <div align="center">
48
+ <img src="https://static.pepy.tech/badge/kerykeion/month" alt="PyPI Downloads">
49
+ <img src="https://static.pepy.tech/badge/kerykeion/week" alt="PyPI Downloads">
50
+ <img src="https://static.pepy.tech/personalized-badge/kerykeion?period=total&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=BLUE&left_text=downloads/total" alt="PyPI Downloads">
51
+ </div>
52
+ <div align="center">
53
+ <img src="https://img.shields.io/pypi/v/kerykeion?label=pypi%20package" alt="Package version">
54
+ <img src="https://img.shields.io/pypi/pyversions/kerykeion.svg" alt="Supported Python versions">
55
+ </div>
56
+ <p align="center">⭐ Like this project? Star it on GitHub and help it grow! ⭐</p>
57
+ &nbsp;
58
+
59
+ Kerykeion is a Python library for astrology. It computes planetary and house positions, detects aspects, and generates SVG charts—including birth, synastry, transit, and composite charts. You can also customize which planets to include in your calculations.
60
+
61
+ The main goal of this project is to offer a clean, data-driven approach to astrology, making it accessible and programmable.
62
+
63
+ Kerykeion also integrates seamlessly with LLM and AI applications.
64
+
65
+ Here is an example of a birthchart:
66
+
67
+ ![John Lenon Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Dark%20Theme%20-%20Natal%20Chart.svg)
68
+
69
+ ## **Web API**
70
+
71
+ If you want to use Kerykeion in a web application or for commercial or *closed-source* purposes, you can try the dedicated web API:
72
+
73
+ **[AstrologerAPI](https://rapidapi.com/gbattaglia/api/astrologer/pricing)**
74
+
75
+ It is [open source](https://github.com/g-battaglia/Astrologer-API) and directly supports this project.
76
+
77
+ ## **Donate**
78
+
79
+ Maintaining this project requires substantial time and effort. The Astrologer API alone cannot cover the costs of full-time development. If you find Kerykeion valuable and would like to support further development, please consider donating:
80
+
81
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/kerykeion)
82
+
83
+ ## Table of Contents
84
+
85
+ - [**Web API**](#web-api)
86
+ - [**Donate**](#donate)
87
+ - [Table of Contents](#table-of-contents)
88
+ - [Installation](#installation)
89
+ - [Quick Start](#quick-start)
90
+ - [Documentation Map](#documentation-map)
91
+ - [Basic Usage](#basic-usage)
92
+ - [Generate a SVG Chart](#generate-a-svg-chart)
93
+ - [Birth Chart](#birth-chart)
94
+ - [External Birth Chart](#external-birth-chart)
95
+ - [Synastry Chart](#synastry-chart)
96
+ - [Transit Chart](#transit-chart)
97
+ - [Solar Return Chart (Dual Wheel)](#solar-return-chart-dual-wheel)
98
+ - [Solar Return Chart (Single Wheel)](#solar-return-chart-single-wheel)
99
+ - [Lunar Return Chart](#lunar-return-chart)
100
+ - [Composite Chart](#composite-chart)
101
+ - [Wheel Only Charts](#wheel-only-charts)
102
+ - [Birth Chart](#birth-chart-1)
103
+ - [Wheel Only Birth Chart (External)](#wheel-only-birth-chart-external)
104
+ - [Synastry Chart](#synastry-chart-1)
105
+ - [Change the Output Directory](#change-the-output-directory)
106
+ - [Change Language](#change-language)
107
+ - [Minified SVG](#minified-svg)
108
+ - [SVG without CSS Variables](#svg-without-css-variables)
109
+ - [Grid Only SVG](#grid-only-svg)
110
+ - [Report Generator](#report-generator)
111
+ - [Quick Examples](#quick-examples)
112
+ - [Section Access](#section-access)
113
+ - [Example: Retrieving Aspects](#example-retrieving-aspects)
114
+ - [Element \& Quality Distribution Strategies](#element--quality-distribution-strategies)
115
+ - [Ayanamsa (Sidereal Modes)](#ayanamsa-sidereal-modes)
116
+ - [House Systems](#house-systems)
117
+ - [Perspective Type](#perspective-type)
118
+ - [Themes](#themes)
119
+ - [Alternative Initialization](#alternative-initialization)
120
+ - [Lunar Nodes (Rahu \& Ketu)](#lunar-nodes-rahu--ketu)
121
+ - [JSON Support](#json-support)
122
+ - [Auto Generated Documentation](#auto-generated-documentation)
123
+ - [Development](#development)
124
+ - [Kerykeion v5.0 – What's New](#kerykeion-v50--whats-new)
125
+ - [🎯 Key Highlights](#-key-highlights)
126
+ - [Factory-Centered Architecture](#factory-centered-architecture)
127
+ - [Pydantic 2 Models \& Type Safety](#pydantic-2-models--type-safety)
128
+ - [Enhanced Features](#enhanced-features)
129
+ - [🚨 Breaking Changes](#-breaking-changes)
130
+ - [1. Removed Legacy Classes](#1-removed-legacy-classes)
131
+ - [2. Import Changes](#2-import-changes)
132
+ - [3. Type Aliases Unified](#3-type-aliases-unified)
133
+ - [4. Lunar Nodes Naming](#4-lunar-nodes-naming)
134
+ - [5. Axis Orb Filtering](#5-axis-orb-filtering)
135
+ - [6. Chart Generation Changes](#6-chart-generation-changes)
136
+ - [7. Aspects API Changes](#7-aspects-api-changes)
137
+ - [🔄 Migration Guide](#-migration-guide)
138
+ - [Using the Backward Compatibility Layer](#using-the-backward-compatibility-layer)
139
+ - [Step-by-Step Migration](#step-by-step-migration)
140
+ - [Automated Migration Script](#automated-migration-script)
141
+ - [📦 Other Notable Changes](#-other-notable-changes)
142
+ - [🎨 New Themes](#-new-themes)
143
+ - [📚 Resources](#-resources)
144
+ - [Integrating Kerykeion into Your Project](#integrating-kerykeion-into-your-project)
145
+ - [License](#license)
146
+ - [Contributing](#contributing)
147
+ - [Citations](#citations)
148
+
149
+ ## Installation
150
+
151
+ Kerykeion requires **Python 3.9** or higher.
152
+
153
+ ```bash
154
+ pip3 install kerykeion
155
+ ```
156
+
157
+ ## Quick Start
158
+
159
+ ```python
160
+ from pathlib import Path
161
+ from kerykeion import AstrologicalSubjectFactory
162
+ from kerykeion.chart_data_factory import ChartDataFactory
163
+ from kerykeion.charts.chart_drawer import ChartDrawer
164
+
165
+ subject = AstrologicalSubjectFactory.from_birth_data(
166
+ name="Example Person",
167
+ year=1990, month=7, day=15,
168
+ hour=10, minute=30,
169
+ lng=12.4964,
170
+ lat=41.9028,
171
+ tz_str="Europe/Rome",
172
+ online=False,
173
+ )
174
+
175
+ chart_data = ChartDataFactory.create_natal_chart_data(subject)
176
+ chart_drawer = ChartDrawer(chart_data=chart_data)
177
+
178
+ output_dir = Path("charts_output")
179
+ output_dir.mkdir(exist_ok=True)
180
+
181
+ chart_drawer.save_svg(output_path=output_dir, filename="example-natal")
182
+ print("Chart saved to", (output_dir / "example-natal.svg").resolve())
183
+ ```
184
+
185
+ This script shows the recommended workflow:
186
+ 1. Create an `AstrologicalSubject` with explicit coordinates and timezone (offline mode).
187
+ 2. Build a `ChartDataModel` through `ChartDataFactory`.
188
+ 3. Render the SVG via `ChartDrawer`, saving it to a controlled folder (`charts_output`).
189
+
190
+ Use the same pattern for synastry, composite, transit, or return charts by swapping the factory method.
191
+
192
+ ## Documentation Map
193
+
194
+ - **README (this file):** Quick start, common recipes, and v5 migration notes.
195
+ - **`site-docs/` (offline Markdown guides):** Deep dives for each factory (`chart_data_factory.md`, `charts.md`, `planetary_return_factory.md`, etc.) with runnable snippets. Run `python scripts/test_markdown_snippets.py site-docs` to validate them locally.
196
+ - **[Auto-generated API Reference](https://www.kerykeion.net/pydocs/kerykeion.html):** Detailed model and function signatures straight from the codebase.
197
+ - **[Kerykeion website](https://www.kerykeion.net/docs/):** Rendered documentation with additional context, tutorials, and showcase material.
198
+
199
+ ## Basic Usage
200
+
201
+ Below is a simple example illustrating the creation of an astrological subject and retrieving astrological details:
202
+
203
+ ```python
204
+ from kerykeion import AstrologicalSubjectFactory
205
+
206
+ # Create an instance of the AstrologicalSubjectFactory class.
207
+ # Arguments: Name, year, month, day, hour, minutes, city, nation
208
+ john = AstrologicalSubjectFactory.from_birth_data(
209
+ "John Lennon", 1940, 10, 9, 18, 30,
210
+ lng=-2.9833,
211
+ lat=53.4,
212
+ tz_str="Europe/London",
213
+ online=False,
214
+ )
215
+
216
+ # Retrieve information about the Sun:
217
+ print(john.sun.model_dump_json())
218
+ # > {"name":"Sun","quality":"Cardinal","element":"Air","sign":"Lib","sign_num":6,"position":16.26789199474399,"abs_pos":196.267891994744,"emoji":"♎️","point_type":"AstrologicalPoint","house":"Sixth_House","retrograde":false}
219
+
220
+ # Retrieve information about the first house:
221
+ print(john.first_house.model_dump_json())
222
+ # > {"name":"First_House","quality":"Cardinal","element":"Fire","sign":"Ari","sign_num":0,"position":19.74676624176799,"abs_pos":19.74676624176799,"emoji":"♈️","point_type":"House","house":null,"retrograde":null}
223
+
224
+ # Retrieve the element of the Moon sign:
225
+ print(john.moon.element)
226
+ # > 'Air'
227
+ ```
228
+
229
+ > **Working offline:** pass `online=False` and specify `lng`, `lat`, and `tz_str` as shown above.
230
+ > **Working online:** set `online=True` and provide `city`, `nation`, and a valid GeoNames username (see `AstrologicalSubjectFactory.from_birth_data()` for details).
231
+
232
+ **To avoid using GeoNames online, specify longitude, latitude, and timezone instead of city and nation:**
233
+
234
+ ```python
235
+ john = AstrologicalSubjectFactory.from_birth_data(
236
+ "John Lennon", 1940, 10, 9, 18, 30,
237
+ lng=-2.9833, # Longitude for Liverpool
238
+ lat=53.4000, # Latitude for Liverpool
239
+ tz_str="Europe/London", # Timezone for Liverpool
240
+ city="Liverpool",
241
+ )
242
+ ```
243
+
244
+ ## Generate a SVG Chart
245
+
246
+ All chart-rendering examples below create a local `charts_output/` folder so the tests can write without touching your home directory. Feel free to change the path when integrating into your own projects.
247
+
248
+ To generate a chart, use the `ChartDataFactory` to pre-compute chart data, then `ChartDrawer` to create the visualization. This two-step process ensures clean separation between astrological calculations and chart rendering.
249
+
250
+ **Tip:**
251
+ The optimized way to open the generated SVG files is with a web browser (e.g., Chrome, Firefox).
252
+ To improve compatibility across different applications, you can use the `remove_css_variables` parameter when generating the SVG. This will inline all styles and eliminate CSS variables, resulting in an SVG that is more broadly supported.
253
+
254
+ ### Birth Chart
255
+
256
+ ```python
257
+ from pathlib import Path
258
+ from kerykeion import AstrologicalSubjectFactory
259
+ from kerykeion.chart_data_factory import ChartDataFactory
260
+ from kerykeion.charts.chart_drawer import ChartDrawer
261
+
262
+ # Step 1: Create subject
263
+ john = AstrologicalSubjectFactory.from_birth_data(
264
+ "John Lennon", 1940, 10, 9, 18, 30,
265
+ lng=-2.9833,
266
+ lat=53.4,
267
+ tz_str="Europe/London",
268
+ online=False,
269
+ )
270
+
271
+ # Step 2: Pre-compute chart data
272
+ chart_data = ChartDataFactory.create_natal_chart_data(john)
273
+
274
+ # Step 3: Create visualization
275
+ birth_chart_svg = ChartDrawer(chart_data=chart_data)
276
+
277
+ output_dir = Path("charts_output")
278
+ output_dir.mkdir(exist_ok=True)
279
+ birth_chart_svg.save_svg(output_path=output_dir, filename="john-lennon-natal")
280
+ ```
281
+
282
+ The SVG file is saved under `charts_output/john-lennon-natal.svg`.
283
+ ![John Lennon Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Natal%20Chart.svg)
284
+
285
+ ### External Birth Chart
286
+
287
+ ```python
288
+ from pathlib import Path
289
+ from kerykeion import AstrologicalSubjectFactory
290
+ from kerykeion.chart_data_factory import ChartDataFactory
291
+ from kerykeion.charts.chart_drawer import ChartDrawer
292
+
293
+ # Step 1: Create subject
294
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
295
+ "John Lennon", 1940, 10, 9, 18, 30,
296
+ lng=-2.9833,
297
+ lat=53.4,
298
+ tz_str="Europe/London",
299
+ online=False,
300
+ )
301
+
302
+ # Step 2: Pre-compute chart data for external natal chart
303
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
304
+
305
+ # Step 3: Create visualization
306
+ birth_chart_svg = ChartDrawer(chart_data=chart_data)
307
+
308
+ output_dir = Path("charts_output")
309
+ output_dir.mkdir(exist_ok=True)
310
+ birth_chart_svg.save_svg(output_path=output_dir, filename="john-lennon-natal-external")
311
+ ```
312
+
313
+ ![John Lennon External Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20ExternalNatal%20-%20Natal%20Chart.svg)
314
+
315
+ ### Synastry Chart
316
+
317
+ ```python
318
+ from pathlib import Path
319
+ from kerykeion import AstrologicalSubjectFactory
320
+ from kerykeion.chart_data_factory import ChartDataFactory
321
+ from kerykeion.charts.chart_drawer import ChartDrawer
322
+
323
+ # Step 1: Create subjects
324
+ first = AstrologicalSubjectFactory.from_birth_data(
325
+ "John Lennon", 1940, 10, 9, 18, 30,
326
+ lng=-2.9833,
327
+ lat=53.4,
328
+ tz_str="Europe/London",
329
+ online=False,
330
+ )
331
+ second = AstrologicalSubjectFactory.from_birth_data(
332
+ "Paul McCartney", 1942, 6, 18, 15, 30,
333
+ lng=-2.9833,
334
+ lat=53.4,
335
+ tz_str="Europe/London",
336
+ online=False,
337
+ )
338
+
339
+ # Step 2: Pre-compute synastry chart data
340
+ chart_data = ChartDataFactory.create_synastry_chart_data(first, second)
341
+
342
+ # Step 3: Create visualization
343
+ synastry_chart = ChartDrawer(chart_data=chart_data)
344
+
345
+ output_dir = Path("charts_output")
346
+ output_dir.mkdir(exist_ok=True)
347
+ synastry_chart.save_svg(output_path=output_dir, filename="lennon-mccartney-synastry")
348
+ ```
349
+
350
+ ![John Lennon and Paul McCartney Synastry](https://www.kerykeion.net/img/examples/synastry-chart.svg)
351
+
352
+ ### Transit Chart
353
+
354
+ ```python
355
+ from pathlib import Path
356
+ from kerykeion import AstrologicalSubjectFactory
357
+ from kerykeion.chart_data_factory import ChartDataFactory
358
+ from kerykeion.charts.chart_drawer import ChartDrawer
359
+
360
+ # Step 1: Create subjects
361
+ transit = AstrologicalSubjectFactory.from_birth_data(
362
+ "Transit", 2025, 6, 8, 8, 45,
363
+ lng=-84.3880,
364
+ lat=33.7490,
365
+ tz_str="America/New_York",
366
+ online=False,
367
+ )
368
+ subject = AstrologicalSubjectFactory.from_birth_data(
369
+ "John Lennon", 1940, 10, 9, 18, 30,
370
+ lng=-2.9833,
371
+ lat=53.4,
372
+ tz_str="Europe/London",
373
+ online=False,
374
+ )
375
+
376
+ # Step 2: Pre-compute transit chart data
377
+ chart_data = ChartDataFactory.create_transit_chart_data(subject, transit)
378
+
379
+ # Step 3: Create visualization
380
+ transit_chart = ChartDrawer(chart_data=chart_data)
381
+
382
+ output_dir = Path("charts_output")
383
+ output_dir.mkdir(exist_ok=True)
384
+ transit_chart.save_svg(output_path=output_dir, filename="john-lennon-transit")
385
+ ```
386
+
387
+ ![John Lennon Transit Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Transit%20Chart.svg)
388
+
389
+ ### Solar Return Chart (Dual Wheel)
390
+
391
+ ```python
392
+ from pathlib import Path
393
+ from kerykeion import AstrologicalSubjectFactory
394
+ from kerykeion.planetary_return_factory import PlanetaryReturnFactory
395
+ from kerykeion.chart_data_factory import ChartDataFactory
396
+ from kerykeion.charts.chart_drawer import ChartDrawer
397
+
398
+ # Step 1: Create natal subject
399
+ john = AstrologicalSubjectFactory.from_birth_data(
400
+ "John Lennon", 1940, 10, 9, 18, 30,
401
+ lng=-2.9833,
402
+ lat=53.4,
403
+ tz_str="Europe/London",
404
+ online=False,
405
+ )
406
+
407
+ # Step 2: Calculate Solar Return subject (offline example with manual coordinates)
408
+ return_factory = PlanetaryReturnFactory(
409
+ john,
410
+ lng=-2.9833,
411
+ lat=53.4000,
412
+ tz_str="Europe/London",
413
+ online=False
414
+ )
415
+ solar_return_subject = return_factory.next_return_from_year(1964, "Solar")
416
+
417
+ # Step 3: Pre-compute return chart data (dual wheel: natal + solar return)
418
+ chart_data = ChartDataFactory.create_return_chart_data(john, solar_return_subject)
419
+
420
+ # Step 4: Create visualization
421
+ solar_return_chart = ChartDrawer(chart_data=chart_data)
422
+
423
+ output_dir = Path("charts_output")
424
+ output_dir.mkdir(exist_ok=True)
425
+ solar_return_chart.save_svg(output_path=output_dir, filename="john-lennon-solar-return-dual")
426
+ ```
427
+
428
+ ![John Lennon Solar Return Chart (Dual Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20DualReturnChart%20Chart%20-%20Solar%20Return.svg)
429
+
430
+ ### Solar Return Chart (Single Wheel)
431
+
432
+ ```python
433
+ from pathlib import Path
434
+ from kerykeion import AstrologicalSubjectFactory
435
+ from kerykeion.planetary_return_factory import PlanetaryReturnFactory
436
+ from kerykeion.chart_data_factory import ChartDataFactory
437
+ from kerykeion.charts.chart_drawer import ChartDrawer
438
+
439
+ # Step 1: Create natal subject
440
+ john = AstrologicalSubjectFactory.from_birth_data(
441
+ "John Lennon", 1940, 10, 9, 18, 30,
442
+ lng=-2.9833,
443
+ lat=53.4,
444
+ tz_str="Europe/London",
445
+ online=False,
446
+ )
447
+
448
+ # Step 2: Calculate Solar Return subject (offline example with manual coordinates)
449
+ return_factory = PlanetaryReturnFactory(
450
+ john,
451
+ lng=-2.9833,
452
+ lat=53.4000,
453
+ tz_str="Europe/London",
454
+ online=False
455
+ )
456
+ solar_return_subject = return_factory.next_return_from_year(1964, "Solar")
457
+
458
+ # Step 3: Build a single-wheel return chart
459
+ chart_data = ChartDataFactory.create_single_wheel_return_chart_data(solar_return_subject)
460
+
461
+ # Step 4: Create visualization
462
+ single_wheel_chart = ChartDrawer(chart_data=chart_data)
463
+
464
+ output_dir = Path("charts_output")
465
+ output_dir.mkdir(exist_ok=True)
466
+ single_wheel_chart.save_svg(output_path=output_dir, filename="john-lennon-solar-return-single")
467
+ ```
468
+
469
+ ![John Lennon Solar Return Chart (Single Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20Solar%20Return%20-%20SingleReturnChart%20Chart.svg)
470
+
471
+ ### Lunar Return Chart
472
+
473
+ ```python
474
+ from pathlib import Path
475
+ from kerykeion import AstrologicalSubjectFactory
476
+ from kerykeion.planetary_return_factory import PlanetaryReturnFactory
477
+ from kerykeion.chart_data_factory import ChartDataFactory
478
+ from kerykeion.charts.chart_drawer import ChartDrawer
479
+
480
+ # Step 1: Create natal subject
481
+ john = AstrologicalSubjectFactory.from_birth_data(
482
+ "John Lennon", 1940, 10, 9, 18, 30,
483
+ lng=-2.9833,
484
+ lat=53.4,
485
+ tz_str="Europe/London",
486
+ online=False,
487
+ )
488
+
489
+ # Step 2: Calculate Lunar Return subject
490
+ return_factory = PlanetaryReturnFactory(
491
+ john,
492
+ lng=-2.9833,
493
+ lat=53.4000,
494
+ tz_str="Europe/London",
495
+ online=False
496
+ )
497
+ lunar_return_subject = return_factory.next_return_from_year(1964, "Lunar")
498
+
499
+ # Step 3: Build a dual wheel (natal + lunar return)
500
+ lunar_return_chart_data = ChartDataFactory.create_return_chart_data(john, lunar_return_subject)
501
+ dual_wheel_chart = ChartDrawer(chart_data=lunar_return_chart_data)
502
+
503
+ output_dir = Path("charts_output")
504
+ output_dir.mkdir(exist_ok=True)
505
+ dual_wheel_chart.save_svg(output_path=output_dir, filename="john-lennon-lunar-return-dual")
506
+
507
+ # Optional: create a single-wheel lunar return
508
+ single_wheel_data = ChartDataFactory.create_single_wheel_return_chart_data(lunar_return_subject)
509
+ single_wheel_chart = ChartDrawer(chart_data=single_wheel_data)
510
+ single_wheel_chart.save_svg(output_path=output_dir, filename="john-lennon-lunar-return-single")
511
+ ```
512
+
513
+ ![John Lennon Lunar Return Chart (Dual Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20DualReturnChart%20Chart%20-%20Lunar%20Return.svg)
514
+
515
+ ![John Lennon Lunar Return Chart (Single Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20Lunar%20Return%20-%20SingleReturnChart%20Chart.svg)
516
+
517
+ ### Composite Chart
518
+
519
+ ```python
520
+ from pathlib import Path
521
+ from kerykeion import CompositeSubjectFactory, AstrologicalSubjectFactory
522
+ from kerykeion.chart_data_factory import ChartDataFactory
523
+ from kerykeion.charts.chart_drawer import ChartDrawer
524
+
525
+ # Step 1: Create subjects (offline configuration)
526
+ angelina = AstrologicalSubjectFactory.from_birth_data(
527
+ "Angelina Jolie", 1975, 6, 4, 9, 9,
528
+ lng=-118.2437,
529
+ lat=34.0522,
530
+ tz_str="America/Los_Angeles",
531
+ online=False,
532
+ )
533
+
534
+ brad = AstrologicalSubjectFactory.from_birth_data(
535
+ "Brad Pitt", 1963, 12, 18, 6, 31,
536
+ lng=-96.7069,
537
+ lat=35.3273,
538
+ tz_str="America/Chicago",
539
+ online=False,
540
+ )
541
+
542
+ # Step 2: Create composite subject
543
+ factory = CompositeSubjectFactory(angelina, brad)
544
+ composite_model = factory.get_midpoint_composite_subject_model()
545
+
546
+ # Step 3: Pre-compute composite chart data
547
+ chart_data = ChartDataFactory.create_composite_chart_data(composite_model)
548
+
549
+ # Step 4: Create visualization
550
+ composite_chart = ChartDrawer(chart_data=chart_data)
551
+
552
+ output_dir = Path("charts_output")
553
+ output_dir.mkdir(exist_ok=True)
554
+ composite_chart.save_svg(output_path=output_dir, filename="jolie-pitt-composite")
555
+ ```
556
+
557
+ ![Angelina Jolie and Brad Pitt Composite Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/Angelina%20Jolie%20and%20Brad%20Pitt%20Composite%20Chart%20-%20Composite%20Chart.svg)
558
+
559
+ ## Wheel Only Charts
560
+
561
+ For _all_ the charts, you can generate a wheel-only chart by using the method `makeWheelOnlySVG()`:
562
+
563
+ ### Birth Chart
564
+
565
+ ```python
566
+ from pathlib import Path
567
+ from kerykeion import AstrologicalSubjectFactory
568
+ from kerykeion.chart_data_factory import ChartDataFactory
569
+ from kerykeion.charts.chart_drawer import ChartDrawer
570
+
571
+ # Step 1: Create subject
572
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
573
+ "John Lennon", 1940, 10, 9, 18, 30,
574
+ lng=-2.9833,
575
+ lat=53.4,
576
+ tz_str="Europe/London",
577
+ online=False,
578
+ )
579
+
580
+ # Step 2: Pre-compute chart data
581
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
582
+
583
+ # Step 3: Create visualization
584
+ birth_chart_svg = ChartDrawer(chart_data=chart_data)
585
+
586
+ output_dir = Path("charts_output")
587
+ output_dir.mkdir(exist_ok=True)
588
+ birth_chart_svg.save_wheel_only_svg_file(output_path=output_dir, filename="john-lennon-natal-wheel")
589
+ ```
590
+
591
+ ![John Lennon Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Wheel%20Only%20-%20Natal%20Chart%20-%20Wheel%20Only.svg)
592
+
593
+ ### Wheel Only Birth Chart (External)
594
+
595
+ ```python
596
+ from pathlib import Path
597
+ from kerykeion import AstrologicalSubjectFactory
598
+ from kerykeion.chart_data_factory import ChartDataFactory
599
+ from kerykeion.charts.chart_drawer import ChartDrawer
600
+
601
+ # Step 1: Create subject
602
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
603
+ "John Lennon", 1940, 10, 9, 18, 30,
604
+ lng=-2.9833,
605
+ lat=53.4,
606
+ tz_str="Europe/London",
607
+ online=False,
608
+ )
609
+
610
+ # Step 2: Pre-compute external natal chart data
611
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
612
+
613
+ # Step 3: Create visualization (external wheel view)
614
+ birth_chart_svg = ChartDrawer(chart_data=chart_data, external_view=True)
615
+
616
+ output_dir = Path("charts_output")
617
+ output_dir.mkdir(exist_ok=True)
618
+ birth_chart_svg.save_wheel_only_svg_file(output_path=output_dir, filename="john-lennon-natal-wheel-external")
619
+ ```
620
+
621
+ ![John Lennon Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Wheel%20External%20Only%20-%20ExternalNatal%20Chart%20-%20Wheel%20Only.svg)
622
+
623
+ ### Synastry Chart
624
+
625
+ ```python
626
+ from pathlib import Path
627
+ from kerykeion import AstrologicalSubjectFactory
628
+ from kerykeion.chart_data_factory import ChartDataFactory
629
+ from kerykeion.charts.chart_drawer import ChartDrawer
630
+
631
+ # Step 1: Create subjects
632
+ first = AstrologicalSubjectFactory.from_birth_data(
633
+ "John Lennon", 1940, 10, 9, 18, 30,
634
+ lng=-2.9833,
635
+ lat=53.4,
636
+ tz_str="Europe/London",
637
+ online=False,
638
+ )
639
+ second = AstrologicalSubjectFactory.from_birth_data(
640
+ "Paul McCartney", 1942, 6, 18, 15, 30,
641
+ lng=-2.9833,
642
+ lat=53.4,
643
+ tz_str="Europe/London",
644
+ online=False,
645
+ )
646
+
647
+ # Step 2: Pre-compute synastry chart data
648
+ chart_data = ChartDataFactory.create_synastry_chart_data(first, second)
649
+
650
+ # Step 3: Create visualization
651
+ synastry_chart = ChartDrawer(chart_data=chart_data)
652
+
653
+ output_dir = Path("charts_output")
654
+ output_dir.mkdir(exist_ok=True)
655
+ synastry_chart.save_wheel_only_svg_file(output_path=output_dir, filename="lennon-mccartney-synastry-wheel")
656
+ ```
657
+
658
+ ![John Lennon and Paul McCartney Synastry](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Wheel%20Synastry%20Only%20-%20Synastry%20Chart%20-%20Wheel%20Only.svg)
659
+
660
+ ### Change the Output Directory
661
+
662
+ To save the SVG file in a custom location, specify the `output_path` parameter in `save_svg()`:
663
+
664
+ ```python
665
+ from pathlib import Path
666
+ from kerykeion import AstrologicalSubjectFactory
667
+ from kerykeion.chart_data_factory import ChartDataFactory
668
+ from kerykeion.charts.chart_drawer import ChartDrawer
669
+
670
+ # Step 1: Create subjects
671
+ first = AstrologicalSubjectFactory.from_birth_data(
672
+ "John Lennon", 1940, 10, 9, 18, 30,
673
+ lng=-2.9833,
674
+ lat=53.4,
675
+ tz_str="Europe/London",
676
+ online=False,
677
+ )
678
+ second = AstrologicalSubjectFactory.from_birth_data(
679
+ "Paul McCartney", 1942, 6, 18, 15, 30,
680
+ lng=-2.9833,
681
+ lat=53.4,
682
+ tz_str="Europe/London",
683
+ online=False,
684
+ )
685
+
686
+ # Step 2: Pre-compute synastry chart data
687
+ chart_data = ChartDataFactory.create_synastry_chart_data(first, second)
688
+
689
+ # Step 3: Create visualization with custom output directory
690
+ synastry_chart = ChartDrawer(chart_data=chart_data)
691
+
692
+ output_dir = Path("my_charts")
693
+ output_dir.mkdir(exist_ok=True)
694
+ synastry_chart.save_svg(output_path=output_dir)
695
+ print("Saved to", (output_dir / f"{synastry_chart.first_obj.name} - Synastry Chart.svg").resolve())
696
+ ```
697
+
698
+ ### Change Language
699
+
700
+ You can switch chart language by passing `chart_language` to the `ChartDrawer` class:
701
+
702
+ ```python
703
+ from pathlib import Path
704
+ from kerykeion import AstrologicalSubjectFactory
705
+ from kerykeion.chart_data_factory import ChartDataFactory
706
+ from kerykeion.charts.chart_drawer import ChartDrawer
707
+
708
+ # Step 1: Create subject
709
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
710
+ "John Lennon", 1940, 10, 9, 18, 30,
711
+ lng=-2.9833,
712
+ lat=53.4,
713
+ tz_str="Europe/London",
714
+ online=False,
715
+ )
716
+
717
+ # Step 2: Pre-compute chart data
718
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
719
+
720
+ # Step 3: Create visualization with Italian language
721
+ birth_chart_svg = ChartDrawer(
722
+ chart_data=chart_data,
723
+ chart_language="IT" # Change to Italian
724
+ )
725
+
726
+ output_dir = Path("charts_output")
727
+ output_dir.mkdir(exist_ok=True)
728
+ birth_chart_svg.save_svg(output_path=output_dir, filename="john-lennon-natal-it")
729
+ ```
730
+
731
+ You can also provide custom labels (or introduce a brand-new language) by passing
732
+ a dictionary to `language_pack`. Only the keys you supply are merged on top of the
733
+ built-in strings:
734
+
735
+ ```python
736
+ from pathlib import Path
737
+ from kerykeion import AstrologicalSubjectFactory
738
+ from kerykeion.chart_data_factory import ChartDataFactory
739
+ from kerykeion.charts.chart_drawer import ChartDrawer
740
+
741
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
742
+ "John Lennon", 1940, 10, 9, 18, 30,
743
+ lng=-2.9833,
744
+ lat=53.4,
745
+ tz_str="Europe/London",
746
+ online=False,
747
+ )
748
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
749
+
750
+ custom_labels = {
751
+ "PT": {
752
+ "info": "Informações",
753
+ "celestial_points": {"Sun": "Sol", "Moon": "Lua"},
754
+ }
755
+ }
756
+
757
+ custom_chart = ChartDrawer(
758
+ chart_data=chart_data,
759
+ chart_language="PT",
760
+ language_pack=custom_labels["PT"],
761
+ )
762
+
763
+ output_dir = Path("charts_output")
764
+ output_dir.mkdir(exist_ok=True)
765
+ custom_chart.save_svg(output_path=output_dir, filename="john-lennon-natal-pt")
766
+ ```
767
+
768
+ More details [here](https://www.kerykeion.net/docs/chart-language).
769
+
770
+ The available languages are:
771
+
772
+ - EN (English)
773
+ - FR (French)
774
+ - PT (Portuguese)
775
+ - ES (Spanish)
776
+ - TR (Turkish)
777
+ - RU (Russian)
778
+ - IT (Italian)
779
+ - CN (Chinese)
780
+ - DE (German)
781
+
782
+ ### Minified SVG
783
+
784
+ To generate a minified SVG, set `minify_svg=True` in the `makeSVG()` method:
785
+
786
+ ```python
787
+ from pathlib import Path
788
+ from kerykeion import AstrologicalSubjectFactory
789
+ from kerykeion.chart_data_factory import ChartDataFactory
790
+ from kerykeion.charts.chart_drawer import ChartDrawer
791
+
792
+ # Step 1: Create subject
793
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
794
+ "John Lennon", 1940, 10, 9, 18, 30,
795
+ lng=-2.9833,
796
+ lat=53.4,
797
+ tz_str="Europe/London",
798
+ online=False,
799
+ )
800
+
801
+ # Step 2: Pre-compute chart data
802
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
803
+
804
+ # Step 3: Create visualization
805
+ birth_chart_svg = ChartDrawer(chart_data=chart_data)
806
+
807
+ output_dir = Path("charts_output")
808
+ output_dir.mkdir(exist_ok=True)
809
+ birth_chart_svg.save_svg(
810
+ output_path=output_dir,
811
+ filename="john-lennon-natal-minified",
812
+ minify=True,
813
+ )
814
+ ```
815
+
816
+ ### SVG without CSS Variables
817
+
818
+ To generate an SVG without CSS variables, set `remove_css_variables=True` in the `makeSVG()` method:
819
+
820
+ ```python
821
+ from pathlib import Path
822
+ from kerykeion import AstrologicalSubjectFactory
823
+ from kerykeion.chart_data_factory import ChartDataFactory
824
+ from kerykeion.charts.chart_drawer import ChartDrawer
825
+
826
+ # Step 1: Create subject
827
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
828
+ "John Lennon", 1940, 10, 9, 18, 30,
829
+ lng=-2.9833,
830
+ lat=53.4,
831
+ tz_str="Europe/London",
832
+ online=False,
833
+ )
834
+
835
+ # Step 2: Pre-compute chart data
836
+ chart_data = ChartDataFactory.create_natal_chart_data(birth_chart)
837
+
838
+ # Step 3: Create visualization
839
+ birth_chart_svg = ChartDrawer(chart_data=chart_data)
840
+
841
+ output_dir = Path("charts_output")
842
+ output_dir.mkdir(exist_ok=True)
843
+ birth_chart_svg.save_svg(
844
+ output_path=output_dir,
845
+ filename="john-lennon-natal-no-css-variables",
846
+ remove_css_variables=True,
847
+ )
848
+ ```
849
+
850
+ This will inline all styles and eliminate CSS variables, resulting in an SVG that is more broadly supported.
851
+
852
+ ### Grid Only SVG
853
+
854
+ It's possible to generate a grid-only SVG, useful for creating a custom layout. To do this, use the `save_aspect_grid_only_svg_file()` method:
855
+
856
+ ```python
857
+ from pathlib import Path
858
+ from kerykeion import AstrologicalSubjectFactory
859
+ from kerykeion.chart_data_factory import ChartDataFactory
860
+ from kerykeion.charts.chart_drawer import ChartDrawer
861
+
862
+ # Step 1: Create subjects
863
+ birth_chart = AstrologicalSubjectFactory.from_birth_data(
864
+ "John Lennon", 1940, 10, 9, 18, 30,
865
+ lng=-2.9833,
866
+ lat=53.4,
867
+ tz_str="Europe/London",
868
+ online=False,
869
+ )
870
+ second = AstrologicalSubjectFactory.from_birth_data(
871
+ "Paul McCartney", 1942, 6, 18, 15, 30,
872
+ lng=-2.9833,
873
+ lat=53.4,
874
+ tz_str="Europe/London",
875
+ online=False,
876
+ )
877
+
878
+ # Step 2: Pre-compute synastry chart data
879
+ chart_data = ChartDataFactory.create_synastry_chart_data(birth_chart, second)
880
+
881
+ # Step 3: Create visualization with dark theme
882
+ aspect_grid_chart = ChartDrawer(chart_data=chart_data, theme="dark")
883
+
884
+ output_dir = Path("charts_output")
885
+ output_dir.mkdir(exist_ok=True)
886
+ aspect_grid_chart.save_aspect_grid_only_svg_file(output_path=output_dir, filename="lennon-mccartney-aspect-grid")
887
+ ```
888
+
889
+ ![John Lennon Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/refs/heads/main/tests/charts/svg/John%20Lennon%20-%20Aspect%20Grid%20Only%20-%20Natal%20Chart%20-%20Aspect%20Grid%20Only.svg)
890
+
891
+ ## Report Generator
892
+
893
+ `ReportGenerator` mirrors the chart-type dispatch of `ChartDrawer`. It accepts raw `AstrologicalSubjectModel` instances as well as any `ChartDataModel` produced by `ChartDataFactory`—including natal, composite, synastry, transit, and planetary return charts—and renders the appropriate textual report automatically.
894
+
895
+ ### Quick Examples
896
+
897
+ ```python
898
+ from kerykeion import ReportGenerator, AstrologicalSubjectFactory, ChartDataFactory
899
+
900
+ # Subject-only report
901
+ subject = AstrologicalSubjectFactory.from_birth_data(
902
+ "Sample Natal", 1990, 7, 21, 14, 45,
903
+ lng=12.4964,
904
+ lat=41.9028,
905
+ tz_str="Europe/Rome",
906
+ online=False,
907
+ )
908
+ ReportGenerator(subject).print_report(include_aspects=False)
909
+
910
+ # Single-chart data (elements, qualities, aspects enabled)
911
+ natal_data = ChartDataFactory.create_natal_chart_data(subject)
912
+ ReportGenerator(natal_data).print_report(max_aspects=10)
913
+
914
+ # Dual-chart data (synastry, transit, dual return, …)
915
+ partner = AstrologicalSubjectFactory.from_birth_data(
916
+ "Sample Partner", 1992, 11, 5, 9, 30,
917
+ lng=12.4964,
918
+ lat=41.9028,
919
+ tz_str="Europe/Rome",
920
+ online=False,
921
+ )
922
+ synastry_data = ChartDataFactory.create_synastry_chart_data(subject, partner)
923
+ ReportGenerator(synastry_data).print_report(max_aspects=12)
924
+ ```
925
+
926
+ Each report contains:
927
+
928
+ - A chart-aware title summarising the subject(s) and chart type
929
+ - Birth/event metadata and configuration settings
930
+ - Celestial points with sign, position, **daily motion**, **declination**, retrograde flag, and house
931
+ - House cusp tables for every subject involved
932
+ - Lunar phase details when available
933
+ - Element/quality distributions and active configuration summaries (for chart data)
934
+ - Aspect listings tailored for single or dual charts, with symbols for type and movement
935
+ - Dual-chart extras such as house comparisons and relationship scores (when provided by the data)
936
+
937
+ ### Section Access
938
+
939
+ All section helpers remain available for targeted output:
940
+
941
+ ```python
942
+ from kerykeion import ReportGenerator, AstrologicalSubjectFactory, ChartDataFactory
943
+
944
+ subject = AstrologicalSubjectFactory.from_birth_data(
945
+ "Sample Natal", 1990, 7, 21, 14, 45,
946
+ lng=12.4964,
947
+ lat=41.9028,
948
+ tz_str="Europe/Rome",
949
+ online=False,
950
+ )
951
+ natal_data = ChartDataFactory.create_natal_chart_data(subject)
952
+
953
+ report = ReportGenerator(natal_data)
954
+ sections = report.generate_report(max_aspects=5).split("\n\n")
955
+ for section in sections[:3]:
956
+ print(section)
957
+ ```
958
+
959
+ Refer to the refreshed [Report Documentation](https://www.kerykeion.net/report/) for end-to-end examples covering every supported chart model.
960
+
961
+ ## Example: Retrieving Aspects
962
+
963
+ Kerykeion provides a unified `AspectsFactory` class for calculating astrological aspects within single charts or between two charts:
964
+
965
+ ```python
966
+ from kerykeion import AspectsFactory, AstrologicalSubjectFactory
967
+
968
+ # Create astrological subjects
969
+ jack = AstrologicalSubjectFactory.from_birth_data(
970
+ "Jack", 1990, 6, 15, 15, 15,
971
+ lng=12.4964,
972
+ lat=41.9028,
973
+ tz_str="Europe/Rome",
974
+ online=False,
975
+ )
976
+ jane = AstrologicalSubjectFactory.from_birth_data(
977
+ "Jane", 1991, 10, 25, 21, 0,
978
+ lng=12.4964,
979
+ lat=41.9028,
980
+ tz_str="Europe/Rome",
981
+ online=False,
982
+ )
983
+
984
+ # For single chart aspects (natal, return, composite, etc.)
985
+ single_chart_result = AspectsFactory.single_chart_aspects(jack)
986
+ print(f"Found {len(single_chart_result.aspects)} aspects in Jack's chart")
987
+ print(single_chart_result.aspects[0])
988
+
989
+ # For dual chart aspects (synastry, transits, comparisons, etc.)
990
+ dual_chart_result = AspectsFactory.dual_chart_aspects(jack, jane)
991
+ print(f"Found {len(dual_chart_result.aspects)} aspects between Jack and Jane's charts")
992
+ print(dual_chart_result.aspects[0])
993
+
994
+ # Each AspectModel includes:
995
+ # - p1_name, p2_name: Planet/point names
996
+ # - aspect: Aspect type (conjunction, trine, square, etc.)
997
+ # - orbit: Orb tolerance in degrees
998
+ # - aspect_degrees: Exact degrees for the aspect (0, 60, 90, 120, 180, etc.)
999
+ # - color: Hex color code for visualization
1000
+ ```
1001
+
1002
+ **Advanced Usage with Custom Settings:**
1003
+
1004
+ ```python
1005
+ # You can also customize aspect calculations with custom orb settings
1006
+ from kerykeion.settings.config_constants import DEFAULT_ACTIVE_ASPECTS
1007
+
1008
+ # Modify aspect settings if needed
1009
+ custom_aspects = DEFAULT_ACTIVE_ASPECTS.copy()
1010
+ # ... modify as needed
1011
+
1012
+ # The factory automatically uses the configured settings for orb calculations
1013
+ # and filters aspects based on relevance and orb thresholds
1014
+ ```
1015
+
1016
+ ## Element & Quality Distribution Strategies
1017
+
1018
+ `ChartDataFactory` now offers two strategies for calculating element and modality totals. The default `"weighted"` mode leans on a curated map that emphasises core factors (for example `sun`, `moon`, and `ascendant` weight 2.0, angles such as `medium_coeli` 1.5, personal planets 1.5, social planets 1.0, outers 0.5, and minor bodies 0.3–0.8). Provide `distribution_method="pure_count"` when you want every active point to contribute equally.
1019
+
1020
+ You can refine the weighting without rebuilding the dictionary: pass lowercase point names to `custom_distribution_weights` and use `"__default__"` to override the fallback value applied to entries that are not listed explicitly.
1021
+
1022
+ ```python
1023
+ from kerykeion import AstrologicalSubjectFactory, ChartDataFactory
1024
+
1025
+ subject = AstrologicalSubjectFactory.from_birth_data(
1026
+ "Sample", 1986, 4, 12, 8, 45,
1027
+ lng=11.3426,
1028
+ lat=44.4949,
1029
+ tz_str="Europe/Rome",
1030
+ online=False,
1031
+ )
1032
+
1033
+ # Equal weighting: every active point counts once
1034
+ pure_data = ChartDataFactory.create_natal_chart_data(
1035
+ subject,
1036
+ distribution_method="pure_count",
1037
+ )
1038
+
1039
+ # Custom emphasis: boost the Sun, soften everything else
1040
+ weighted_data = ChartDataFactory.create_natal_chart_data(
1041
+ subject,
1042
+ distribution_method="weighted",
1043
+ custom_distribution_weights={
1044
+ "sun": 3.0,
1045
+ "__default__": 0.75,
1046
+ },
1047
+ )
1048
+
1049
+ print(pure_data.element_distribution.fire)
1050
+ print(weighted_data.element_distribution.fire)
1051
+ ```
1052
+
1053
+ All convenience helpers (`create_synastry_chart_data`, `create_transit_chart_data`, returns, and composites) forward the same keyword-only parameters, so you can keep a consistent weighting scheme across every chart type.
1054
+
1055
+ For an extended walkthrough (including category breakdowns of the default map), see `site-docs/element_quality_distribution.md`.
1056
+
1057
+ ## Ayanamsa (Sidereal Modes)
1058
+
1059
+ By default, the zodiac type is **Tropical**. To use **Sidereal**, specify the sidereal mode:
1060
+
1061
+ ```python
1062
+ johnny = AstrologicalSubjectFactory.from_birth_data(
1063
+ "Johnny Depp", 1963, 6, 9, 0, 0,
1064
+ lng=-87.1112,
1065
+ lat=37.7719,
1066
+ tz_str="America/Chicago",
1067
+ online=False,
1068
+ zodiac_type="Sidereal",
1069
+ sidereal_mode="LAHIRI"
1070
+ )
1071
+ ```
1072
+
1073
+ More examples [here](https://www.kerykeion.net/docs//sidereal-modes/).
1074
+
1075
+ Full list of supported sidereal modes [here](https://www.kerykeion.net/pydocs/kerykeion/schemas/kr_literals.html#SiderealMode).
1076
+
1077
+ ## House Systems
1078
+
1079
+ By default, houses are calculated using **Placidus**. Configure a different house system as follows:
1080
+
1081
+ ```python
1082
+ johnny = AstrologicalSubjectFactory.from_birth_data(
1083
+ "Johnny Depp", 1963, 6, 9, 0, 0,
1084
+ lng=-87.1112,
1085
+ lat=37.7719,
1086
+ tz_str="America/Chicago",
1087
+ online=False,
1088
+ houses_system_identifier="M"
1089
+ )
1090
+ ```
1091
+
1092
+ More examples [here](https://www.kerykeion.net/docs//houses-systems/).
1093
+
1094
+ Full list of supported house systems [here](https://www.kerykeion.net/pydocs/kerykeion/schemas/kr_literals.html#HousesSystem).
1095
+
1096
+ So far all the available houses system in the Swiss Ephemeris are supported but the Gauquelin Sectors.
1097
+
1098
+ ## Perspective Type
1099
+
1100
+ By default, Kerykeion uses the **Apparent Geocentric** perspective (the most standard in astrology). Other perspectives (e.g., **Heliocentric**) can be set this way:
1101
+
1102
+ ```python
1103
+ johnny = AstrologicalSubjectFactory.from_birth_data(
1104
+ "Johnny Depp", 1963, 6, 9, 0, 0,
1105
+ lng=-87.1112,
1106
+ lat=37.7719,
1107
+ tz_str="America/Chicago",
1108
+ online=False,
1109
+ perspective_type="Heliocentric"
1110
+ )
1111
+ ```
1112
+
1113
+ More examples [here](https://www.kerykeion.net/docs//perspective-type/).
1114
+
1115
+ Full list of supported perspective types [here](https://www.kerykeion.net/pydocs/kerykeion/schemas/kr_literals.html#PerspectiveType).
1116
+
1117
+ ## Themes
1118
+
1119
+ Kerykeion provides several chart themes:
1120
+
1121
+ - **Classic** (default)
1122
+ - **Dark**
1123
+ - **Dark High Contrast**
1124
+ - **Light**
1125
+ - **Strawberry**
1126
+ - **Black & White** (optimized for monochrome printing)
1127
+
1128
+ Each theme offers a distinct visual style, allowing you to choose the one that best suits your preferences or presentation needs. If you prefer more control over the appearance, you can opt not to set any theme, making it easier to customize the chart by overriding the default CSS variables. For more detailed instructions on how to apply themes, check the [documentation](https://www.kerykeion.net/docs/theming)
1129
+
1130
+ The Black & White theme renders glyphs, rings, and aspects in solid black on light backgrounds, designed for crisp B/W prints (PDF or paper) without sacrificing legibility.
1131
+
1132
+ Here's an example of how to set the theme:
1133
+
1134
+ ```python
1135
+ from pathlib import Path
1136
+ from kerykeion import AstrologicalSubjectFactory
1137
+ from kerykeion.chart_data_factory import ChartDataFactory
1138
+ from kerykeion.charts.chart_drawer import ChartDrawer
1139
+
1140
+ # Step 1: Create subject
1141
+ dark_theme_subject = AstrologicalSubjectFactory.from_birth_data(
1142
+ "John Lennon - Dark Theme", 1940, 10, 9, 18, 30,
1143
+ lng=-2.9833,
1144
+ lat=53.4,
1145
+ tz_str="Europe/London",
1146
+ online=False,
1147
+ )
1148
+
1149
+ # Step 2: Pre-compute chart data
1150
+ chart_data = ChartDataFactory.create_natal_chart_data(dark_theme_subject)
1151
+
1152
+ # Step 3: Create visualization with dark high contrast theme
1153
+ dark_theme_natal_chart = ChartDrawer(chart_data=chart_data, theme="dark-high-contrast")
1154
+
1155
+ output_dir = Path("charts_output")
1156
+ output_dir.mkdir(exist_ok=True)
1157
+ dark_theme_natal_chart.save_svg(output_path=output_dir, filename="john-lennon-natal-dark-high-contrast")
1158
+ ```
1159
+
1160
+ ![John Lennon](https://www.kerykeion.net/img/showcase/John%20Lennon%20-%20Dark%20-%20Natal%20Chart.svg)
1161
+
1162
+ ## Alternative Initialization
1163
+
1164
+ Create an `AstrologicalSubjectModel` from a UTC ISO 8601 string:
1165
+
1166
+ ```python
1167
+ from kerykeion import AstrologicalSubjectFactory
1168
+
1169
+ subject = AstrologicalSubjectFactory.from_iso_utc_time(
1170
+ name="Johnny Depp",
1171
+ iso_utc_time="1963-06-09T05:00:00Z",
1172
+ city="Owensboro",
1173
+ nation="US",
1174
+ lng=-87.1112,
1175
+ lat=37.7719,
1176
+ tz_str="America/Chicago",
1177
+ online=False,
1178
+ )
1179
+
1180
+ print(subject.iso_formatted_local_datetime)
1181
+ ```
1182
+
1183
+ If you prefer automatic geocoding, set `online=True` and provide your GeoNames credentials via `geonames_username`.
1184
+
1185
+ ## Lunar Nodes (Rahu & Ketu)
1186
+
1187
+ Kerykeion supports both **True** and **Mean** Lunar Nodes:
1188
+
1189
+ - **True North Lunar Node**: `"true_node"` (name kept without "north" for backward compatibility).
1190
+ - **True South Lunar Node**: `"true_south_node"`.
1191
+ - **Mean North Lunar Node**: `"mean_node"` (name kept without "north" for backward compatibility).
1192
+ - **Mean South Lunar Node**: `"mean_south_node"`.
1193
+
1194
+ In instances of the classes used to generate aspects and SVG charts, only the mean nodes are active. To activate the true nodes, you need to pass the `active_points` parameter to the `ChartDataFactory` methods.
1195
+
1196
+ Example:
1197
+
1198
+ ```python
1199
+ from pathlib import Path
1200
+ from kerykeion import AstrologicalSubjectFactory
1201
+ from kerykeion.chart_data_factory import ChartDataFactory
1202
+ from kerykeion.charts.chart_drawer import ChartDrawer
1203
+
1204
+ # Step 1: Create subject
1205
+ subject = AstrologicalSubjectFactory.from_birth_data(
1206
+ "John Lennon", 1940, 10, 9, 18, 30,
1207
+ lng=-2.9833,
1208
+ lat=53.4,
1209
+ tz_str="Europe/London",
1210
+ online=False,
1211
+ )
1212
+
1213
+ # Step 2: Pre-compute chart data with custom active points including true nodes
1214
+ chart_data = ChartDataFactory.create_natal_chart_data(
1215
+ subject,
1216
+ active_points=[
1217
+ "Sun",
1218
+ "Moon",
1219
+ "Mercury",
1220
+ "Venus",
1221
+ "Mars",
1222
+ "Jupiter",
1223
+ "Saturn",
1224
+ "Uranus",
1225
+ "Neptune",
1226
+ "Pluto",
1227
+ "Mean_Node",
1228
+ "Mean_South_Node",
1229
+ "True_Node", # Activates True North Node
1230
+ "True_South_Node", # Activates True South Node
1231
+ "Ascendant",
1232
+ "Medium_Coeli",
1233
+ "Descendant",
1234
+ "Imum_Coeli"
1235
+ ]
1236
+ )
1237
+
1238
+ # Step 3: Create visualization
1239
+ chart = ChartDrawer(chart_data=chart_data)
1240
+
1241
+ output_dir = Path("charts_output")
1242
+ output_dir.mkdir(exist_ok=True)
1243
+ chart.save_svg(output_path=output_dir, filename="johnny-depp-custom-points")
1244
+ ```
1245
+
1246
+ ## JSON Support
1247
+
1248
+ You can serialize the astrological subject (the base data used throughout the library) to JSON:
1249
+
1250
+ ```python
1251
+ from kerykeion import AstrologicalSubjectFactory
1252
+
1253
+ johnny = AstrologicalSubjectFactory.from_birth_data(
1254
+ "Johnny Depp", 1963, 6, 9, 0, 0,
1255
+ lng=-87.1112,
1256
+ lat=37.7719,
1257
+ tz_str="America/Chicago",
1258
+ online=False,
1259
+ )
1260
+
1261
+ print(johnny.model_dump_json(indent=2))
1262
+ ```
1263
+
1264
+ ## Auto Generated Documentation
1265
+
1266
+ You can find auto-generated documentation [here](https://www.kerykeion.net/pydocs/kerykeion.html). Most classes and functions include docstrings.
1267
+
1268
+ ## Development
1269
+
1270
+ Clone the repository or download the ZIP via the GitHub interface.
1271
+
1272
+ ## Kerykeion v5.0 – What's New
1273
+
1274
+ Kerykeion v5 is a **complete redesign** that modernizes the library with a data-first approach, factory-based architecture, and Pydantic 2 models. This version brings significant improvements in API design, type safety, and extensibility.
1275
+
1276
+ ### 🎯 Key Highlights
1277
+
1278
+ #### Factory-Centered Architecture
1279
+
1280
+ The old class-based approach has been replaced with a modern factory pattern:
1281
+
1282
+ - **`AstrologicalSubjectFactory`**: Replaces the old `AstrologicalSubject` class
1283
+ - **`ChartDataFactory`**: Pre-computes enriched chart data (elements, qualities, aspects)
1284
+ - **`ChartDrawer`**: Pure SVG rendering separated from calculations
1285
+ - **`AspectsFactory`**: Unified aspects calculation for natal and synastry charts
1286
+ - **`PlanetaryReturnFactory`**: Solar and Lunar returns computation
1287
+ - **`HouseComparisonFactory`**: House overlay analysis for synastry
1288
+ - **`RelationshipScoreFactory`**: Compatibility scoring between charts
1289
+
1290
+ **Old v4 API:**
1291
+
1292
+ ```python
1293
+ from pathlib import Path
1294
+ from kerykeion import AstrologicalSubject, KerykeionChartSVG
1295
+
1296
+ # v4 - Class-based approach
1297
+ output_dir = Path("charts_output")
1298
+ output_dir.mkdir(exist_ok=True)
1299
+
1300
+ subject = AstrologicalSubject(
1301
+ "John", 1990, 1, 1, 12, 0,
1302
+ lng=-0.1276,
1303
+ lat=51.5074,
1304
+ tz_str="Europe/London",
1305
+ online=False,
1306
+ )
1307
+ chart = KerykeionChartSVG(subject, new_output_directory=output_dir)
1308
+ chart.makeSVG()
1309
+ ```
1310
+
1311
+ **New v5 API:**
1312
+
1313
+ ```python
1314
+ from pathlib import Path
1315
+ from kerykeion import AstrologicalSubjectFactory, ChartDataFactory, ChartDrawer
1316
+
1317
+ # v5 - Factory-based approach with separation of concerns
1318
+ subject = AstrologicalSubjectFactory.from_birth_data(
1319
+ "John", 1990, 1, 1, 12, 0,
1320
+ lng=-0.1276,
1321
+ lat=51.5074,
1322
+ tz_str="Europe/London",
1323
+ online=False,
1324
+ )
1325
+ chart_data = ChartDataFactory.create_natal_chart_data(subject)
1326
+ drawer = ChartDrawer(chart_data=chart_data)
1327
+
1328
+ output_dir = Path("charts_output")
1329
+ output_dir.mkdir(exist_ok=True)
1330
+ drawer.save_svg(output_path=output_dir, filename="john-factory-demo")
1331
+ ```
1332
+
1333
+ #### Pydantic 2 Models & Type Safety
1334
+
1335
+ All data structures are now strongly typed Pydantic models:
1336
+
1337
+ - **`AstrologicalSubjectModel`**: Subject data with full validation
1338
+ - **`ChartDataModel`**: Enriched chart data with elements, qualities, aspects
1339
+ - **`AspectModel` list**: Raw aspect entries directly on `ChartDataModel.aspects`
1340
+ - **`PlanetReturnModel`**: Planetary return data
1341
+ - **`ElementDistributionModel`**: Element statistics (fire, earth, air, water)
1342
+ - **`QualityDistributionModel`**: Quality statistics (cardinal, fixed, mutable)
1343
+
1344
+ All models support:
1345
+
1346
+ - JSON serialization/deserialization
1347
+ - Dictionary export
1348
+ - Subscript access
1349
+ - Full IDE autocomplete and type checking
1350
+
1351
+ #### Enhanced Features
1352
+
1353
+ - **Speed & Declination**: All celestial points now include daily motion speed and declination
1354
+ - **Element & Quality Analysis**: Automatic calculation of element/quality distributions
1355
+ - **Relationship Scoring**: Built-in compatibility analysis for synastry
1356
+ - **House Comparison**: Detailed house overlay analysis
1357
+ - **Transit Time Ranges**: Advanced transit tracking over time periods
1358
+ - **Report Module**: Comprehensive text reports with ASCII tables
1359
+ - **Axis Orb Control**: Chart axes now share the same orb as planets by default; pass the keyword-only `axis_orb_limit` to return to a traditional, tighter axis filtering when you need it.
1360
+ - **Element Weight Strategies**: Element and quality stats now default to a curated weighted balance; pass `distribution_method` or `custom_distribution_weights` when you need equal counts or bespoke weightings (including a `__default__` fallback) across any chart factory helper.
1361
+
1362
+ ### 🚨 Breaking Changes
1363
+
1364
+ #### 1. Removed Legacy Classes
1365
+
1366
+ The following classes have been **completely removed** and must be replaced:
1367
+
1368
+ | Removed (v4) | Replacement (v5) |
1369
+ | ---------------------- | ---------------------------------------------- |
1370
+ | `AstrologicalSubject` | `AstrologicalSubjectFactory.from_birth_data()` |
1371
+ | `KerykeionChartSVG` | `ChartDrawer` + `ChartDataFactory` |
1372
+ | `NatalAspects` | `AspectsFactory.single_chart_aspects()` |
1373
+ | `SynastryAspects` | `AspectsFactory.dual_chart_aspects()` |
1374
+ | `relationship_score()` | `RelationshipScoreFactory` |
1375
+
1376
+ **Note**: The `kerykeion.backword` module provides temporary wrappers for `AstrologicalSubject` and `KerykeionChartSVG` with deprecation warnings. These will be **removed in v6.0**.
1377
+
1378
+ #### 2. Import Changes
1379
+
1380
+ Module structure has been completely reorganized:
1381
+
1382
+ **Old imports (v4):**
1383
+
1384
+ ```python
1385
+ from kerykeion import AstrologicalSubject, KerykeionChartSVG
1386
+ from kerykeion.kr_types import KerykeionException
1387
+ from kerykeion.kr_types.kr_literals import Planet, AxialCusps
1388
+ ```
1389
+
1390
+ **New imports (v5):**
1391
+
1392
+ ```python
1393
+ from kerykeion import AstrologicalSubjectFactory, ChartDataFactory, ChartDrawer
1394
+ from kerykeion.schemas import KerykeionException
1395
+ from kerykeion.schemas.kr_literals import AstrologicalPoint
1396
+ ```
1397
+
1398
+ **Backward compatibility (v5 only, removed in v6.0):**
1399
+
1400
+ ```python
1401
+ # Old kr_types imports still work with deprecation warnings
1402
+ from kerykeion.kr_types import Planet, AxialCusps # Shows warning
1403
+ from kerykeion.schemas import Planet, AxialCusps # Works, no warning
1404
+ ```
1405
+
1406
+ #### 3. Type Aliases Unified
1407
+
1408
+ **Old (v4):** `Planet` and `AxialCusps` were separate types
1409
+ **New (v5):** Unified as `AstrologicalPoint`
1410
+
1411
+ ```python
1412
+ # v4
1413
+ from kerykeion.kr_types.kr_literals import Planet, AxialCusps
1414
+
1415
+ # v5 (recommended)
1416
+ from kerykeion.schemas.kr_literals import AstrologicalPoint
1417
+
1418
+ # v5 (transition, uses aliases)
1419
+ from kerykeion.schemas import Planet, AxialCusps # Still available
1420
+ ```
1421
+
1422
+ #### 4. Lunar Nodes Naming
1423
+
1424
+ All lunar node fields have been renamed for clarity:
1425
+
1426
+ | Old Name (v4) | New Name (v5) |
1427
+ | ----------------- | ----------------------- |
1428
+ | `Mean_Node` | `Mean_North_Lunar_Node` |
1429
+ | `True_Node` | `True_North_Lunar_Node` |
1430
+ | `Mean_South_Node` | `Mean_South_Lunar_Node` |
1431
+ | `True_South_Node` | `True_South_Lunar_Node` |
1432
+
1433
+ **Migration example:**
1434
+
1435
+ ```python
1436
+ from kerykeion import AstrologicalSubject, AstrologicalSubjectFactory
1437
+
1438
+ # v4 alias (still available, emits DeprecationWarning)
1439
+ legacy_subject = AstrologicalSubject(
1440
+ "John", 1990, 1, 1, 12, 0,
1441
+ lng=-0.1276,
1442
+ lat=51.5074,
1443
+ tz_str="Europe/London",
1444
+ online=False,
1445
+ )
1446
+ print(legacy_subject.mean_node)
1447
+
1448
+ # v5 canonical name
1449
+ modern_subject = AstrologicalSubjectFactory.from_birth_data(
1450
+ "John", 1990, 1, 1, 12, 0,
1451
+ lng=-0.1276,
1452
+ lat=51.5074,
1453
+ tz_str="Europe/London",
1454
+ online=False,
1455
+ )
1456
+ print(modern_subject.mean_north_lunar_node)
1457
+ ```
1458
+
1459
+ #### 5. Axis Orb Filtering
1460
+
1461
+ Modern default orbs now treat chart axes (ASC, MC, DSC, IC) exactly like planets. If you prefer a traditional, constrained approach, every public aspect factory exposes the keyword-only `axis_orb_limit` parameter so you can set a dedicated threshold when needed.
1462
+
1463
+ #### 6. Chart Generation Changes
1464
+
1465
+ The two-step process (data + rendering) is now required:
1466
+
1467
+ **Old v4:**
1468
+
1469
+ ```python
1470
+ from pathlib import Path
1471
+ from kerykeion import AstrologicalSubject, KerykeionChartSVG
1472
+
1473
+ output_dir = Path("charts_output")
1474
+ output_dir.mkdir(exist_ok=True)
1475
+
1476
+ subject = AstrologicalSubject(
1477
+ "John", 1990, 1, 1, 12, 0,
1478
+ lng=-0.1276,
1479
+ lat=51.5074,
1480
+ tz_str="Europe/London",
1481
+ online=False,
1482
+ )
1483
+ chart = KerykeionChartSVG(subject, new_output_directory=output_dir)
1484
+ chart.makeSVG()
1485
+ ```
1486
+
1487
+ **New v5:**
1488
+
1489
+ ```python
1490
+ from pathlib import Path
1491
+ from kerykeion import AstrologicalSubjectFactory, ChartDataFactory
1492
+ from kerykeion.charts.chart_drawer import ChartDrawer
1493
+
1494
+ subject = AstrologicalSubjectFactory.from_birth_data(
1495
+ "John", 1990, 1, 1, 12, 0,
1496
+ lng=-0.1276,
1497
+ lat=51.5074,
1498
+ tz_str="Europe/London",
1499
+ online=False,
1500
+ )
1501
+ chart_data = ChartDataFactory.create_natal_chart_data(subject)
1502
+ drawer = ChartDrawer(chart_data=chart_data)
1503
+
1504
+ output_dir = Path("charts_output")
1505
+ output_dir.mkdir(exist_ok=True)
1506
+ drawer.save_svg(output_path=output_dir, filename="john-v5-demo")
1507
+ ```
1508
+
1509
+ #### 7. Aspects API Changes
1510
+
1511
+ Aspects are now calculated through the factory:
1512
+
1513
+ **Old v4:**
1514
+
1515
+ ```python
1516
+ from kerykeion import AstrologicalSubjectFactory, NatalAspects, SynastryAspects
1517
+
1518
+ subject = AstrologicalSubjectFactory.from_birth_data(
1519
+ "John", 1990, 1, 1, 12, 0,
1520
+ lng=-0.1276,
1521
+ lat=51.5074,
1522
+ tz_str="Europe/London",
1523
+ online=False,
1524
+ )
1525
+ subject1 = subject
1526
+ subject2 = AstrologicalSubjectFactory.from_birth_data(
1527
+ "Jane", 1990, 6, 5, 8, 30,
1528
+ lng=-0.1276,
1529
+ lat=51.5074,
1530
+ tz_str="Europe/London",
1531
+ online=False,
1532
+ )
1533
+
1534
+ natal_aspects = NatalAspects(subject)
1535
+ synastry_aspects = SynastryAspects(subject1, subject2)
1536
+ ```
1537
+
1538
+ **New v5:**
1539
+
1540
+ ```python
1541
+ from kerykeion import AstrologicalSubjectFactory, AspectsFactory
1542
+
1543
+ subject = AstrologicalSubjectFactory.from_birth_data(
1544
+ "John", 1990, 1, 1, 12, 0,
1545
+ lng=-0.1276,
1546
+ lat=51.5074,
1547
+ tz_str="Europe/London",
1548
+ online=False,
1549
+ )
1550
+ subject1 = subject
1551
+ subject2 = AstrologicalSubjectFactory.from_birth_data(
1552
+ "Jane", 1990, 6, 5, 8, 30,
1553
+ lng=-0.1276,
1554
+ lat=51.5074,
1555
+ tz_str="Europe/London",
1556
+ online=False,
1557
+ )
1558
+
1559
+ natal_aspects = AspectsFactory.single_chart_aspects(subject)
1560
+ synastry_aspects = AspectsFactory.dual_chart_aspects(subject1, subject2)
1561
+ ```
1562
+
1563
+ Note (v5.1): The two lists `relevant_aspects` and `all_aspects` were unified into a single, cleaned list: `aspects`. If you previously used either property, switch to `aspects`. The legacy properties still work via the backward-compatibility layer but both return the same unified list.
1564
+
1565
+ ### 🔄 Migration Guide
1566
+
1567
+ #### Using the Backward Compatibility Layer
1568
+
1569
+ For a gradual migration, use the `kerykeion.backword` module:
1570
+
1571
+ ```python
1572
+ from kerykeion import AstrologicalSubject # Legacy wrapper
1573
+
1574
+ subject = AstrologicalSubject(
1575
+ "John Doe", 1990, 1, 1, 12, 0,
1576
+ lng=-0.1276,
1577
+ lat=51.5074,
1578
+ tz_str="Europe/London",
1579
+ online=False,
1580
+ )
1581
+
1582
+ # These still work but show DeprecationWarnings
1583
+ print(subject.mean_node) # Maps to mean_north_lunar_node
1584
+ print(subject.true_node) # Maps to true_north_lunar_node
1585
+ ```
1586
+
1587
+ **⚠️ Warning**: This compatibility layer will be **removed in v6.0**.
1588
+
1589
+ #### Step-by-Step Migration
1590
+
1591
+ 1. **Update imports**
1592
+
1593
+ ```python
1594
+ # Old (v4)
1595
+ from kerykeion import AstrologicalSubject, KerykeionChartSVG
1596
+
1597
+ # New (v5)
1598
+ from kerykeion import AstrologicalSubjectFactory, ChartDataFactory, ChartDrawer
1599
+ ```
1600
+
1601
+ 2. **Update subject creation**
1602
+
1603
+ ```python
1604
+ from kerykeion import AstrologicalSubject, AstrologicalSubjectFactory
1605
+
1606
+ # Old (v4)
1607
+ subject = AstrologicalSubject(
1608
+ "John", 1990, 1, 1, 12, 0,
1609
+ lng=-0.1276,
1610
+ lat=51.5074,
1611
+ tz_str="Europe/London",
1612
+ online=False,
1613
+ )
1614
+
1615
+ # New (v5)
1616
+ subject = AstrologicalSubjectFactory.from_birth_data(
1617
+ "John", 1990, 1, 1, 12, 0,
1618
+ lng=-0.1276,
1619
+ lat=51.5074,
1620
+ tz_str="Europe/London",
1621
+ online=False,
1622
+ )
1623
+ ```
1624
+
1625
+ 3. **Update chart generation**
1626
+
1627
+ ```python
1628
+ from pathlib import Path
1629
+ from kerykeion import AstrologicalSubject, AstrologicalSubjectFactory, ChartDataFactory, KerykeionChartSVG
1630
+ from kerykeion.charts.chart_drawer import ChartDrawer
1631
+
1632
+ # Old (v4)
1633
+ legacy_subject = AstrologicalSubject(
1634
+ "John", 1990, 1, 1, 12, 0,
1635
+ lng=-0.1276,
1636
+ lat=51.5074,
1637
+ tz_str="Europe/London",
1638
+ online=False,
1639
+ )
1640
+ output_dir = Path("charts_output")
1641
+ output_dir.mkdir(exist_ok=True)
1642
+
1643
+ chart = KerykeionChartSVG(legacy_subject, new_output_directory=output_dir)
1644
+ chart.makeSVG()
1645
+
1646
+ # New (v5)
1647
+ modern_subject = AstrologicalSubjectFactory.from_birth_data(
1648
+ "John", 1990, 1, 1, 12, 0,
1649
+ lng=-0.1276,
1650
+ lat=51.5074,
1651
+ tz_str="Europe/London",
1652
+ online=False,
1653
+ )
1654
+ chart_data = ChartDataFactory.create_natal_chart_data(modern_subject)
1655
+ drawer = ChartDrawer(chart_data=chart_data)
1656
+ drawer.save_svg(output_path=output_dir, filename="john-v5-migration")
1657
+ ```
1658
+
1659
+ 4. **Update field access** (lunar nodes)
1660
+
1661
+ ```python
1662
+ from kerykeion import AstrologicalSubject, AstrologicalSubjectFactory
1663
+
1664
+ # Old (v4)
1665
+ legacy_subject = AstrologicalSubject(
1666
+ "John", 1990, 1, 1, 12, 0,
1667
+ lng=-0.1276,
1668
+ lat=51.5074,
1669
+ tz_str="Europe/London",
1670
+ online=False,
1671
+ )
1672
+ legacy_mean_node = legacy_subject.mean_node
1673
+ print(getattr(legacy_mean_node, "position", "Legacy mean node not active"))
1674
+
1675
+ # New (v5)
1676
+ modern_subject = AstrologicalSubjectFactory.from_birth_data(
1677
+ "John", 1990, 1, 1, 12, 0,
1678
+ lng=-0.1276,
1679
+ lat=51.5074,
1680
+ tz_str="Europe/London",
1681
+ online=False,
1682
+ )
1683
+ modern_mean_node = modern_subject.mean_north_lunar_node
1684
+ print(getattr(modern_mean_node, "position", "Modern mean node not active"))
1685
+ ```
1686
+
1687
+ 5. **Update aspects**
1688
+
1689
+ ```python
1690
+ from kerykeion import AstrologicalSubject, AstrologicalSubjectFactory, NatalAspects, AspectsFactory
1691
+
1692
+ # Old (v4)
1693
+ legacy_subject = AstrologicalSubject(
1694
+ "John", 1990, 1, 1, 12, 0,
1695
+ lng=-0.1276,
1696
+ lat=51.5074,
1697
+ tz_str="Europe/London",
1698
+ online=False,
1699
+ )
1700
+ legacy_aspects = NatalAspects(legacy_subject)
1701
+ print(f"Legacy aspects count: {len(legacy_aspects.relevant_aspects)}")
1702
+
1703
+ # New (v5)
1704
+ modern_subject = AstrologicalSubjectFactory.from_birth_data(
1705
+ "John", 1990, 1, 1, 12, 0,
1706
+ lng=-0.1276,
1707
+ lat=51.5074,
1708
+ tz_str="Europe/London",
1709
+ online=False,
1710
+ )
1711
+ modern_aspects = AspectsFactory.single_chart_aspects(modern_subject)
1712
+ print(f"Modern aspects count: {len(modern_aspects.aspects)}")
1713
+ ```
1714
+
1715
+ #### Automated Migration Script
1716
+
1717
+ Use this sed script to update Python files automatically:
1718
+
1719
+ ```bash
1720
+ # Update lunar node references
1721
+ find . -name "*.py" -type f -exec sed -i.bak \
1722
+ -e 's/\.mean_node/.mean_north_lunar_node/g' \
1723
+ -e 's/\.true_node/.true_north_lunar_node/g' \
1724
+ -e 's/\.mean_south_node/.mean_south_lunar_node/g' \
1725
+ -e 's/\.true_south_node/.true_south_lunar_node/g' \
1726
+ -e 's/"Mean_Node"/"Mean_North_Lunar_Node"/g' \
1727
+ -e 's/"True_Node"/"True_North_Lunar_Node"/g' \
1728
+ -e 's/"Mean_South_Node"/"Mean_South_Lunar_Node"/g' \
1729
+ -e 's/"True_South_Node"/"True_South_Lunar_Node"/g' \
1730
+ {} \;
1731
+ ```
1732
+
1733
+ **Note**: Always review automated changes and test thoroughly before committing.
1734
+
1735
+ ### 📦 Other Notable Changes
1736
+
1737
+ - **Packaging**: Migrated from Poetry to PEP 621 + Hatchling with `uv.lock`
1738
+ - **Settings**: Centralized in `kerykeion.schemas` and `kerykeion.settings`
1739
+ - **Configuration**: Default chart presets consolidated in `kerykeion/settings/chart_defaults.py`
1740
+ - **Type System**: All literals consolidated in `kr_literals.py`
1741
+ - **Performance**: Caching improvements with `functools.lru_cache`
1742
+ - **Testing**: 376 tests with 87% coverage, regenerated fixtures for v5
1743
+
1744
+ ### 🎨 New Themes
1745
+
1746
+ Additional chart themes added:
1747
+
1748
+ - `classic` (default)
1749
+ - `dark`
1750
+ - `dark-high-contrast`
1751
+ - `light`
1752
+ - `strawberry`
1753
+ - `black-and-white`
1754
+
1755
+ ### 📚 Resources
1756
+
1757
+ - **Full Release Notes**: [v5.0.0.md](release_notes/v5.0.0b1.md)
1758
+ - **Documentation**: [kerykeion.readthedocs.io](https://kerykeion.readthedocs.io)
1759
+ - **API Reference**: [kerykeion.net/pydocs](https://www.kerykeion.net/pydocs/kerykeion.html)
1760
+ - **Examples**: See the `examples/` folder for runnable code
1761
+ - **Support**: [GitHub Discussions](https://github.com/g-battaglia/kerykeion/discussions)
1762
+
1763
+ **Migration Timeline:**
1764
+
1765
+ - **v5.0**: Current - Backward compatibility layer available
1766
+ - **v6.0**: Future - Compatibility layer will be removed
1767
+
1768
+ ## Integrating Kerykeion into Your Project
1769
+
1770
+ If you would like to incorporate Kerykeion's astrological features into your application, please reach out via [email](mailto:kerykeion.astrology@gmail.com?subject=Integration%20Request). Whether you need custom features, support, or specialized consulting, I am happy to discuss potential collaborations.
1771
+
1772
+ ## License
1773
+
1774
+ This project is covered under the AGPL-3.0 License. For detailed information, please see the [LICENSE](LICENSE) file. If you have questions, feel free to contact me at [kerykeion.astrology@gmail.com](mailto:kerykeion.astrology@gmail.com?subject=Kerykeion).
1775
+
1776
+ As a rule of thumb, if you use this library in a project, you should open-source that project under a compatible license. Alternatively, if you wish to keep your source closed, consider using the [AstrologerAPI](https://rapidapi.com/gbattaglia/api/astrologer/), which is AGPL-3.0 compliant and also helps support the project.
1777
+
1778
+ Since the AstrologerAPI is an external third-party service, using it does _not_ require your code to be open-source.
1779
+
1780
+ ## Contributing
1781
+
1782
+ Contributions are welcome! Feel free to submit pull requests or report issues.
1783
+
1784
+ By submitting a contribution, you agree to assign the copyright of that contribution to the maintainer. The project stays openly available under the AGPL for everyone, while the re-licensing option helps sustain future development. Your authorship remains acknowledged in the commit history and release notes.
1785
+
1786
+ ## Citations
1787
+
1788
+ If using Kerykeion in published or academic work, please cite as follows:
1789
+
1790
+ ```
1791
+ Battaglia, G. (2025). Kerykeion: A Python Library for Astrological Calculations and Chart Generation.
1792
+ https://github.com/g-battaglia/kerykeion
1793
+ ```