astrolith 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +317 -0
- data/astrolith.gemspec +35 -0
- data/examples/example_usage.rb +154 -0
- data/lib/ruby_chart_engine/calculations/aspects.rb +88 -0
- data/lib/ruby_chart_engine/calculations/dignities.rb +71 -0
- data/lib/ruby_chart_engine/calculations/houses.rb +97 -0
- data/lib/ruby_chart_engine/calculations/positions.rb +113 -0
- data/lib/ruby_chart_engine/charts/base_chart.rb +135 -0
- data/lib/ruby_chart_engine/charts/composite.rb +90 -0
- data/lib/ruby_chart_engine/charts/natal.rb +79 -0
- data/lib/ruby_chart_engine/charts/progressed.rb +55 -0
- data/lib/ruby_chart_engine/charts/solar_return.rb +90 -0
- data/lib/ruby_chart_engine/charts/transit.rb +64 -0
- data/lib/ruby_chart_engine/input/coordinates.rb +49 -0
- data/lib/ruby_chart_engine/input/datetime.rb +68 -0
- data/lib/ruby_chart_engine/input/timezone.rb +76 -0
- data/lib/ruby_chart_engine/serializers/json_serializer.rb +44 -0
- data/lib/ruby_chart_engine/version.rb +3 -0
- data/lib/ruby_chart_engine.rb +82 -0
- metadata +177 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0d189aaf9ea9a6f0c78bd37ef2db4d5c466ed2dfb6ddaa167d57a7a66ee1704d
|
|
4
|
+
data.tar.gz: fd4a37d35802c4c6646dc37e805921d0095d3802c7e162fcce553ca94ef5468b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b66c0604a9c413c15fefd9054a93e97baab4ed63940fdc20ce7bcc50d2ad0c53c3fb727d79a9a4ba617ddead19e1461a5a19912e0cdbda69dafa16238bc6f2cb
|
|
7
|
+
data.tar.gz: 61f675bb33986226be7385fa20f6cd0cc58574add760ade7bc91f5ed363f7a127f642ed1fa596c0829a0f898e1db90d0b91e3b1f427f0627f854323f6a5580e4
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Go-pye
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# Astrolith
|
|
2
|
+
|
|
3
|
+
A Ruby port of the [immanuel-python](https://github.com/theriftlab/immanuel-python) astrology library, functioning as a decoupled data calculation engine with structured JSON output.
|
|
4
|
+
|
|
5
|
+
[]()
|
|
6
|
+
[]()
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Multiple Chart Types**: Natal, Solar Return, Progressed, Composite, Transit
|
|
11
|
+
- **Comprehensive Data**: Planets, houses, aspects, dignities, decans, moon phase, chart shape
|
|
12
|
+
- **Swiss Ephemeris**: High-precision astronomical calculations using built-in Moshier ephemeris
|
|
13
|
+
- **No External Files Required**: Uses analytical ephemeris (precision: ~0.1 arc seconds for planets, ~3" for Moon)
|
|
14
|
+
- **JSON Output**: Structured data output for easy integration
|
|
15
|
+
- **Flexible Input**: Multiple formats for coordinates, datetime, and timezone
|
|
16
|
+
- **7 House Systems**: Placidus, Koch, Whole Sign, Equal, Porphyrius, Regiomontanus, Campanus
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Add this line to your application's Gemfile:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
gem 'astrolith'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And then execute:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bundle install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or install it yourself as:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
gem install astrolith
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
require 'ruby_chart_engine'
|
|
42
|
+
|
|
43
|
+
# Create a natal chart
|
|
44
|
+
chart = RubyChartEngine::Charts::Natal.new(
|
|
45
|
+
datetime: '1990-05-15T14:30:00',
|
|
46
|
+
latitude: 40.7128,
|
|
47
|
+
longitude: -74.0060,
|
|
48
|
+
timezone: 'America/New_York',
|
|
49
|
+
house_system: :placidus
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Access chart data
|
|
53
|
+
puts chart.planets[:sun][:sign][:name] # => "Taurus"
|
|
54
|
+
puts chart.planets[:sun][:house] # => 10
|
|
55
|
+
puts chart.moon_phase # => "Waxing Gibbous"
|
|
56
|
+
puts chart.chart_shape # => "bowl"
|
|
57
|
+
|
|
58
|
+
# Export to JSON
|
|
59
|
+
json = chart.to_json
|
|
60
|
+
puts json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage Examples
|
|
64
|
+
|
|
65
|
+
### Natal Chart
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
chart = RubyChartEngine::Charts::Natal.new(
|
|
69
|
+
datetime: '1990-05-15T14:30:00',
|
|
70
|
+
latitude: 40.7128,
|
|
71
|
+
longitude: -74.0060,
|
|
72
|
+
timezone: 'America/New_York'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Access planetary data
|
|
76
|
+
sun = chart.planets[:sun]
|
|
77
|
+
# => {
|
|
78
|
+
# longitude: 54.123,
|
|
79
|
+
# sign_longitude: 24.123,
|
|
80
|
+
# sign: { name: "Taurus", element: "Earth", modality: "Fixed" },
|
|
81
|
+
# house: 10,
|
|
82
|
+
# decan: 3,
|
|
83
|
+
# movement: "direct",
|
|
84
|
+
# dignities: { domicile: false, exaltation: false, ... }
|
|
85
|
+
# }
|
|
86
|
+
|
|
87
|
+
# Check aspects
|
|
88
|
+
chart.aspects.each do |aspect|
|
|
89
|
+
puts "#{aspect[:planet1]} #{aspect[:type]} #{aspect[:planet2]} (orb: #{aspect[:orb]}�)"
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Solar Return
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
solar_return = RubyChartEngine::Charts::SolarReturn.new(
|
|
97
|
+
natal_datetime: '1990-05-15T14:30:00',
|
|
98
|
+
return_year: 2024,
|
|
99
|
+
latitude: 40.7128,
|
|
100
|
+
longitude: -74.0060,
|
|
101
|
+
timezone: 'America/New_York'
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Progressed Chart
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
progressed = RubyChartEngine::Charts::Progressed.new(
|
|
109
|
+
natal_datetime: '1990-05-15T14:30:00',
|
|
110
|
+
progression_date: '2024-03-15',
|
|
111
|
+
latitude: 40.7128,
|
|
112
|
+
longitude: -74.0060,
|
|
113
|
+
timezone: 'America/New_York'
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Composite Chart
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
composite = RubyChartEngine::Charts::Composite.new(
|
|
121
|
+
chart1_params: {
|
|
122
|
+
datetime: '1990-05-15T14:30:00',
|
|
123
|
+
latitude: 40.7128,
|
|
124
|
+
longitude: -74.0060,
|
|
125
|
+
timezone: 'America/New_York'
|
|
126
|
+
},
|
|
127
|
+
chart2_params: {
|
|
128
|
+
datetime: '1992-08-20T10:00:00',
|
|
129
|
+
latitude: 34.0522,
|
|
130
|
+
longitude: -118.2437,
|
|
131
|
+
timezone: 'America/Los_Angeles'
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Transit Chart
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
natal = RubyChartEngine::Charts::Natal.new(
|
|
140
|
+
datetime: '1990-05-15T14:30:00',
|
|
141
|
+
latitude: 40.7128,
|
|
142
|
+
longitude: -74.0060,
|
|
143
|
+
timezone: 'America/New_York'
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
transit = RubyChartEngine::Charts::Transit.new(
|
|
147
|
+
natal_chart_params: natal,
|
|
148
|
+
transit_datetime: Time.now.iso8601
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# View transit aspects
|
|
152
|
+
transit.transit_aspects.each do |aspect|
|
|
153
|
+
puts "#{aspect[:planet1]} #{aspect[:type]} #{aspect[:planet2]}"
|
|
154
|
+
end
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Input Formats
|
|
158
|
+
|
|
159
|
+
### Coordinates
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# Decimal degrees
|
|
163
|
+
latitude: 40.7128, longitude: -74.0060
|
|
164
|
+
|
|
165
|
+
# Text format (degrees and minutes)
|
|
166
|
+
latitude: '40n43', longitude: '74w00'
|
|
167
|
+
|
|
168
|
+
# Mixed formats work too
|
|
169
|
+
latitude: 40.7128, longitude: '74w00'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### DateTime
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# ISO 8601 string
|
|
176
|
+
datetime: '1990-05-15T14:30:00'
|
|
177
|
+
|
|
178
|
+
# Hash
|
|
179
|
+
datetime: { year: 1990, month: 5, day: 15, hour: 14, minute: 30 }
|
|
180
|
+
|
|
181
|
+
# Ruby DateTime object
|
|
182
|
+
datetime: DateTime.new(1990, 5, 15, 14, 30, 0)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### House Systems
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
# Available systems:
|
|
189
|
+
house_system: :placidus # Default
|
|
190
|
+
house_system: :koch
|
|
191
|
+
house_system: :whole_sign
|
|
192
|
+
house_system: :equal
|
|
193
|
+
house_system: :porphyrius
|
|
194
|
+
house_system: :regiomontanus
|
|
195
|
+
house_system: :campanus
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Output Structure
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"metadata": {
|
|
203
|
+
"datetime": "1990-05-15T14:30:00+00:00",
|
|
204
|
+
"julian_day": 2448025.1041666665,
|
|
205
|
+
"latitude": 40.7128,
|
|
206
|
+
"longitude": -74.006,
|
|
207
|
+
"timezone": "America/New_York",
|
|
208
|
+
"house_system": "placidus"
|
|
209
|
+
},
|
|
210
|
+
"planets": {
|
|
211
|
+
"sun": {
|
|
212
|
+
"longitude": 54.123,
|
|
213
|
+
"sign_longitude": 24.123,
|
|
214
|
+
"sign": { "name": "Taurus", "element": "Earth", "modality": "Fixed" },
|
|
215
|
+
"house": 10,
|
|
216
|
+
"decan": 3,
|
|
217
|
+
"movement": "direct",
|
|
218
|
+
"dignities": { "domicile": false, "exaltation": false, ... }
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"houses": { "cusps": [...] },
|
|
222
|
+
"angles": { "ascendant": {...}, "midheaven": {...}, ... },
|
|
223
|
+
"aspects": [...],
|
|
224
|
+
"moon_phase": "Waxing Gibbous",
|
|
225
|
+
"chart_shape": "bowl"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Celestial Objects
|
|
230
|
+
|
|
231
|
+
**Planets**: Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
|
|
232
|
+
|
|
233
|
+
**Points**: North Node, South Node *(Note: Chiron is not available when using the built-in Moshier ephemeris)*
|
|
234
|
+
|
|
235
|
+
**Angles**: Ascendant, Midheaven, Descendant, Imum Coeli
|
|
236
|
+
|
|
237
|
+
## Aspects
|
|
238
|
+
|
|
239
|
+
Supported aspects with configurable orbs:
|
|
240
|
+
- Conjunction (0�)
|
|
241
|
+
- Opposition (180�)
|
|
242
|
+
- Trine (120�)
|
|
243
|
+
- Square (90�)
|
|
244
|
+
- Sextile (60�)
|
|
245
|
+
- Quincunx (150�)
|
|
246
|
+
- Semi-Sextile (30�)
|
|
247
|
+
- Semi-Square (45�)
|
|
248
|
+
- Sesquiquadrate (135�)
|
|
249
|
+
|
|
250
|
+
## Testing
|
|
251
|
+
|
|
252
|
+
Run the test suite:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
bundle exec rspec
|
|
256
|
+
|
|
257
|
+
# With documentation format
|
|
258
|
+
bundle exec rspec --format documentation
|
|
259
|
+
|
|
260
|
+
# Run specific test file
|
|
261
|
+
bundle exec rspec spec/charts/natal_spec.rb
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Dependencies
|
|
265
|
+
|
|
266
|
+
- **swe4r**: Swiss Ephemeris C extension for astronomical calculations
|
|
267
|
+
- **ephemeris**: Higher-level astrological logic built on swe4r
|
|
268
|
+
- **daru**: Data Analysis in Ruby for data structuring
|
|
269
|
+
- **tzinfo**: Timezone handling
|
|
270
|
+
|
|
271
|
+
### Optional Visualization
|
|
272
|
+
|
|
273
|
+
- **apexcharts**: For analytical charts
|
|
274
|
+
- **prawn**: For PDF and geometric drawing (chart wheels)
|
|
275
|
+
|
|
276
|
+
## Documentation
|
|
277
|
+
|
|
278
|
+
- [Quick Start Guide](docs/QUICK_START.md) - Get started quickly with common use cases
|
|
279
|
+
- [Project Overview](docs/PROJECT_OVERVIEW.md) - Architecture and implementation details
|
|
280
|
+
- [Implementation Summary](docs/IMPLEMENTATION_SUMMARY.md) - Technical implementation notes
|
|
281
|
+
|
|
282
|
+
## Development
|
|
283
|
+
|
|
284
|
+
After checking out the repo:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Install dependencies
|
|
288
|
+
bundle install
|
|
289
|
+
|
|
290
|
+
# Run tests
|
|
291
|
+
bundle exec rspec
|
|
292
|
+
|
|
293
|
+
# Run console for experimentation
|
|
294
|
+
bundle console
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Contributing
|
|
298
|
+
|
|
299
|
+
1. Fork it
|
|
300
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
301
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
302
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
303
|
+
5. Create new Pull Request
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT License
|
|
308
|
+
|
|
309
|
+
## Acknowledgments
|
|
310
|
+
|
|
311
|
+
This library is a Ruby port of [immanuel-python](https://github.com/theriftlab/immanuel-python), maintaining compatibility with its JSON output structure while leveraging Ruby's ecosystem.
|
|
312
|
+
|
|
313
|
+
## Notes
|
|
314
|
+
|
|
315
|
+
- This implementation uses the **built-in Moshier ephemeris** (analytical calculations) which provides excellent precision without requiring external ephemeris files
|
|
316
|
+
- Chiron is not available with the Moshier ephemeris (would require external `.se1` files)
|
|
317
|
+
- For production use with extended date ranges or maximum precision, consider using the full Swiss Ephemeris with external data files
|
data/astrolith.gemspec
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Gem::Specification.new do |spec|
|
|
2
|
+
spec.name = "astrolith"
|
|
3
|
+
spec.version = "0.1.0"
|
|
4
|
+
spec.authors = ["Go-pye"]
|
|
5
|
+
spec.email = ["Go-pye@users.noreply.github.com"]
|
|
6
|
+
|
|
7
|
+
spec.summary = "A Ruby port of immanuel-python astrology library"
|
|
8
|
+
spec.description = "Astrolith is a decoupled astrological data calculation engine that generates structured JSON output. It supports multiple chart types including Natal, Solar Return, Progressed, Composite, and Transit charts."
|
|
9
|
+
spec.homepage = "https://github.com/Go-pye/astrolith"
|
|
10
|
+
spec.license = "MIT"
|
|
11
|
+
|
|
12
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
13
|
+
|
|
14
|
+
spec.files = Dir.glob("{lib,examples}/**/*") + %w[
|
|
15
|
+
README.md
|
|
16
|
+
LICENSE
|
|
17
|
+
astrolith.gemspec
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
spec.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
# Core dependencies
|
|
23
|
+
spec.add_dependency "swe4r", "~> 0.0.2"
|
|
24
|
+
spec.add_dependency "ephemeris", "~> 0.1"
|
|
25
|
+
spec.add_dependency "daru", "~> 0.3"
|
|
26
|
+
spec.add_dependency "tzinfo", "~> 2.0"
|
|
27
|
+
|
|
28
|
+
# Optional visualization dependencies
|
|
29
|
+
spec.add_dependency "apexcharts", "~> 0.1"
|
|
30
|
+
spec.add_dependency "prawn", "~> 2.4"
|
|
31
|
+
|
|
32
|
+
# Development dependencies
|
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.12"
|
|
34
|
+
spec.add_development_dependency "pry", "~> 0.14"
|
|
35
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/ruby_chart_engine'
|
|
4
|
+
|
|
5
|
+
# Example 1: Basic Natal Chart
|
|
6
|
+
puts "=" * 80
|
|
7
|
+
puts "Example 1: Basic Natal Chart"
|
|
8
|
+
puts "=" * 80
|
|
9
|
+
|
|
10
|
+
natal_chart = RubyChartEngine::Charts::Natal.new(
|
|
11
|
+
datetime: '1990-05-15T14:30:00',
|
|
12
|
+
latitude: 40.7128,
|
|
13
|
+
longitude: -74.0060,
|
|
14
|
+
timezone: 'America/New_York',
|
|
15
|
+
house_system: :placidus
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
puts "\nSun Position:"
|
|
19
|
+
sun = natal_chart.planets[:sun]
|
|
20
|
+
puts " Longitude: #{sun[:longitude].round(2)}°"
|
|
21
|
+
puts " Sign: #{sun[:sign][:name]}"
|
|
22
|
+
puts " House: #{sun[:house]}"
|
|
23
|
+
puts " Movement: #{sun[:movement]}"
|
|
24
|
+
|
|
25
|
+
puts "\nMoon Position:"
|
|
26
|
+
moon = natal_chart.planets[:moon]
|
|
27
|
+
puts " Longitude: #{moon[:longitude].round(2)}°"
|
|
28
|
+
puts " Sign: #{moon[:sign][:name]}"
|
|
29
|
+
puts " House: #{moon[:house]}"
|
|
30
|
+
|
|
31
|
+
puts "\nMoon Phase: #{natal_chart.moon_phase}"
|
|
32
|
+
puts "Chart Shape: #{natal_chart.chart_shape}"
|
|
33
|
+
|
|
34
|
+
puts "\nAspects (first 3):"
|
|
35
|
+
natal_chart.aspects.take(3).each do |aspect|
|
|
36
|
+
puts " #{aspect[:planet1]} #{aspect[:type]} #{aspect[:planet2]} (orb: #{aspect[:orb].round(2)}°)"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Example 2: Solar Return Chart
|
|
40
|
+
puts "\n" + "=" * 80
|
|
41
|
+
puts "Example 2: Solar Return Chart for 2024"
|
|
42
|
+
puts "=" * 80
|
|
43
|
+
|
|
44
|
+
solar_return = RubyChartEngine::Charts::SolarReturn.new(
|
|
45
|
+
natal_datetime: '1990-05-15T14:30:00',
|
|
46
|
+
return_year: 2024,
|
|
47
|
+
latitude: 40.7128,
|
|
48
|
+
longitude: -74.0060,
|
|
49
|
+
timezone: 'America/New_York'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
puts "\nSolar Return Date: #{solar_return.datetime.datetime}"
|
|
53
|
+
puts "Natal Sun Longitude: #{solar_return.natal_sun_longitude.round(2)}°"
|
|
54
|
+
puts "Solar Return Sun: #{solar_return.planets[:sun][:longitude].round(2)}°"
|
|
55
|
+
|
|
56
|
+
# Example 3: Composite Chart
|
|
57
|
+
puts "\n" + "=" * 80
|
|
58
|
+
puts "Example 3: Composite Chart"
|
|
59
|
+
puts "=" * 80
|
|
60
|
+
|
|
61
|
+
composite = RubyChartEngine::Charts::Composite.new(
|
|
62
|
+
chart1_params: {
|
|
63
|
+
datetime: '1990-05-15T14:30:00',
|
|
64
|
+
latitude: 40.7128,
|
|
65
|
+
longitude: -74.0060,
|
|
66
|
+
timezone: 'America/New_York'
|
|
67
|
+
},
|
|
68
|
+
chart2_params: {
|
|
69
|
+
datetime: '1992-08-20T10:00:00',
|
|
70
|
+
latitude: 34.0522,
|
|
71
|
+
longitude: -118.2437,
|
|
72
|
+
timezone: 'America/Los_Angeles'
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
puts "\nComposite Sun:"
|
|
77
|
+
comp_sun = composite.planets[:sun]
|
|
78
|
+
puts " Longitude: #{comp_sun[:longitude].round(2)}°"
|
|
79
|
+
puts " Sign: #{comp_sun[:sign][:name]}"
|
|
80
|
+
puts " House: #{comp_sun[:house]}"
|
|
81
|
+
|
|
82
|
+
# Example 4: Transit Chart
|
|
83
|
+
puts "\n" + "=" * 80
|
|
84
|
+
puts "Example 4: Transit Chart"
|
|
85
|
+
puts "=" * 80
|
|
86
|
+
|
|
87
|
+
transit = RubyChartEngine::Charts::Transit.new(
|
|
88
|
+
natal_chart_params: natal_chart,
|
|
89
|
+
transit_datetime: '2024-03-15T12:00:00'
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
puts "\nTransit Aspects to Natal Chart (first 5):"
|
|
93
|
+
transit.transit_aspects.take(5).each do |aspect|
|
|
94
|
+
puts " Transit #{aspect[:planet1]} #{aspect[:type]} Natal #{aspect[:planet2]} (orb: #{aspect[:orb].round(2)}°)"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Example 5: JSON Export
|
|
98
|
+
puts "\n" + "=" * 80
|
|
99
|
+
puts "Example 5: JSON Export"
|
|
100
|
+
puts "=" * 80
|
|
101
|
+
|
|
102
|
+
puts "\nNatal Chart as JSON (pretty):"
|
|
103
|
+
json = RubyChartEngine::Serializers::JsonSerializer.serialize_pretty(natal_chart)
|
|
104
|
+
puts json[0..500] + "..."
|
|
105
|
+
|
|
106
|
+
puts "\nChart with filtered output (metadata and planets only):"
|
|
107
|
+
filtered_json = RubyChartEngine::Serializers::JsonSerializer.serialize_with_options(
|
|
108
|
+
natal_chart,
|
|
109
|
+
include: [:metadata, :planets],
|
|
110
|
+
pretty: true
|
|
111
|
+
)
|
|
112
|
+
puts filtered_json[0..300] + "..."
|
|
113
|
+
|
|
114
|
+
# Example 6: Different Coordinate Formats
|
|
115
|
+
puts "\n" + "=" * 80
|
|
116
|
+
puts "Example 6: Different Coordinate Formats"
|
|
117
|
+
puts "=" * 80
|
|
118
|
+
|
|
119
|
+
# Standard text format
|
|
120
|
+
chart_text = RubyChartEngine::Charts::Natal.new(
|
|
121
|
+
datetime: '1990-05-15T14:30:00',
|
|
122
|
+
latitude: '40n43',
|
|
123
|
+
longitude: '74w00',
|
|
124
|
+
timezone: 'America/New_York'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
puts "\nChart with text coordinates:"
|
|
128
|
+
puts " Latitude: #{chart_text.coordinates.latitude.round(4)}°"
|
|
129
|
+
puts " Longitude: #{chart_text.coordinates.longitude.round(4)}°"
|
|
130
|
+
|
|
131
|
+
# Example 7: Different House Systems
|
|
132
|
+
puts "\n" + "=" * 80
|
|
133
|
+
puts "Example 7: Different House Systems"
|
|
134
|
+
puts "=" * 80
|
|
135
|
+
|
|
136
|
+
house_systems = [:placidus, :koch, :whole_sign, :equal]
|
|
137
|
+
|
|
138
|
+
house_systems.each do |system|
|
|
139
|
+
chart = RubyChartEngine::Charts::Natal.new(
|
|
140
|
+
datetime: '1990-05-15T14:30:00',
|
|
141
|
+
latitude: 40.7128,
|
|
142
|
+
longitude: -74.0060,
|
|
143
|
+
timezone: 'America/New_York',
|
|
144
|
+
house_system: system
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
puts "\n#{system.to_s.capitalize} House System:"
|
|
148
|
+
puts " 1st House Cusp: #{chart.houses[:cusps][0].round(2)}°"
|
|
149
|
+
puts " 10th House Cusp: #{chart.houses[:cusps][9].round(2)}°"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
puts "\n" + "=" * 80
|
|
153
|
+
puts "Examples Complete!"
|
|
154
|
+
puts "=" * 80
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module RubyChartEngine
|
|
2
|
+
module Calculations
|
|
3
|
+
class Aspects
|
|
4
|
+
# Standard aspect definitions with orbs
|
|
5
|
+
ASPECT_TYPES = {
|
|
6
|
+
conjunction: { angle: 0, orb: 8 },
|
|
7
|
+
opposition: { angle: 180, orb: 8 },
|
|
8
|
+
trine: { angle: 120, orb: 8 },
|
|
9
|
+
square: { angle: 90, orb: 8 },
|
|
10
|
+
sextile: { angle: 60, orb: 6 },
|
|
11
|
+
quincunx: { angle: 150, orb: 3 },
|
|
12
|
+
semi_sextile: { angle: 30, orb: 3 },
|
|
13
|
+
semi_square: { angle: 45, orb: 3 },
|
|
14
|
+
sesquiquadrate: { angle: 135, orb: 3 }
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# Calculate all aspects between two sets of planets
|
|
18
|
+
def self.calculate(planets1, planets2 = nil)
|
|
19
|
+
planets2 ||= planets1
|
|
20
|
+
aspects = []
|
|
21
|
+
|
|
22
|
+
planets1.each do |name1, data1|
|
|
23
|
+
planets2.each do |name2, data2|
|
|
24
|
+
# Skip if comparing planet to itself
|
|
25
|
+
next if planets2.equal?(planets1) && name1 == name2
|
|
26
|
+
|
|
27
|
+
# Skip if we've already calculated this pair (for same chart)
|
|
28
|
+
next if planets2.equal?(planets1) && planets1.keys.index(name1) >= planets1.keys.index(name2)
|
|
29
|
+
|
|
30
|
+
aspect = find_aspect(
|
|
31
|
+
data1[:longitude],
|
|
32
|
+
data2[:longitude],
|
|
33
|
+
name1,
|
|
34
|
+
name2
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
aspects << aspect if aspect
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
aspects
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Find aspect between two longitudes
|
|
45
|
+
def self.find_aspect(lon1, lon2, name1, name2)
|
|
46
|
+
angle = calculate_angle(lon1, lon2)
|
|
47
|
+
|
|
48
|
+
ASPECT_TYPES.each do |type, config|
|
|
49
|
+
diff = (angle - config[:angle]).abs
|
|
50
|
+
|
|
51
|
+
if diff <= config[:orb]
|
|
52
|
+
return {
|
|
53
|
+
type: type,
|
|
54
|
+
planet1: name1,
|
|
55
|
+
planet2: name2,
|
|
56
|
+
angle: config[:angle],
|
|
57
|
+
orb: diff,
|
|
58
|
+
applying: false # TODO: Calculate if aspect is applying or separating
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Calculate the angle between two longitudes
|
|
67
|
+
def self.calculate_angle(lon1, lon2)
|
|
68
|
+
diff = (lon2 - lon1).abs
|
|
69
|
+
diff = 360 - diff if diff > 180
|
|
70
|
+
diff
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Determine if aspect is applying or separating based on speeds
|
|
74
|
+
def self.applying?(planet1_data, planet2_data, aspect_angle)
|
|
75
|
+
# Get speeds
|
|
76
|
+
speed1 = planet1_data[:speed_longitude]
|
|
77
|
+
speed2 = planet2_data[:speed_longitude]
|
|
78
|
+
|
|
79
|
+
# If speeds are moving toward exact aspect, it's applying
|
|
80
|
+
# This is a simplified calculation
|
|
81
|
+
relative_speed = speed2 - speed1
|
|
82
|
+
|
|
83
|
+
# If faster planet is behind slower planet, aspect is applying
|
|
84
|
+
relative_speed > 0
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module RubyChartEngine
|
|
2
|
+
module Calculations
|
|
3
|
+
class Dignities
|
|
4
|
+
# Essential dignities table
|
|
5
|
+
RULERSHIPS = {
|
|
6
|
+
aries: { ruler: :mars, detriment: :venus, exaltation: :sun, fall: :saturn },
|
|
7
|
+
taurus: { ruler: :venus, detriment: :mars, exaltation: :moon, fall: nil },
|
|
8
|
+
gemini: { ruler: :mercury, detriment: :jupiter, exaltation: nil, fall: nil },
|
|
9
|
+
cancer: { ruler: :moon, detriment: :saturn, exaltation: :jupiter, fall: :mars },
|
|
10
|
+
leo: { ruler: :sun, detriment: :saturn, exaltation: nil, fall: nil },
|
|
11
|
+
virgo: { ruler: :mercury, detriment: :jupiter, exaltation: :mercury, fall: :venus },
|
|
12
|
+
libra: { ruler: :venus, detriment: :mars, exaltation: :saturn, fall: :sun },
|
|
13
|
+
scorpio: { ruler: :mars, detriment: :venus, exaltation: nil, fall: :moon },
|
|
14
|
+
sagittarius: { ruler: :jupiter, detriment: :mercury, exaltation: nil, fall: nil },
|
|
15
|
+
capricorn: { ruler: :saturn, detriment: :moon, exaltation: :mars, fall: :jupiter },
|
|
16
|
+
aquarius: { ruler: :saturn, detriment: :sun, exaltation: nil, fall: nil },
|
|
17
|
+
pisces: { ruler: :jupiter, detriment: :mercury, exaltation: :venus, fall: :mercury }
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# Modern rulerships (for outer planets)
|
|
21
|
+
MODERN_RULERSHIPS = {
|
|
22
|
+
scorpio: :pluto,
|
|
23
|
+
aquarius: :uranus,
|
|
24
|
+
pisces: :neptune
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Calculate dignities for a planet in a sign
|
|
28
|
+
def self.calculate(planet_name, sign_index)
|
|
29
|
+
sign_name = SIGNS[sign_index][:name].downcase.to_sym
|
|
30
|
+
dignities_for_sign = RULERSHIPS[sign_name]
|
|
31
|
+
|
|
32
|
+
return default_dignities unless dignities_for_sign
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
domicile: dignities_for_sign[:ruler] == planet_name,
|
|
36
|
+
detriment: dignities_for_sign[:detriment] == planet_name,
|
|
37
|
+
exaltation: dignities_for_sign[:exaltation] == planet_name,
|
|
38
|
+
fall: dignities_for_sign[:fall] == planet_name,
|
|
39
|
+
peregrine: !has_any_dignity?(planet_name, dignities_for_sign)
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Calculate dignity score
|
|
44
|
+
def self.score(dignities)
|
|
45
|
+
score = 0
|
|
46
|
+
score += 5 if dignities[:domicile]
|
|
47
|
+
score += 4 if dignities[:exaltation]
|
|
48
|
+
score -= 5 if dignities[:detriment]
|
|
49
|
+
score -= 4 if dignities[:fall]
|
|
50
|
+
score
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def self.has_any_dignity?(planet_name, dignities_for_sign)
|
|
56
|
+
dignities_for_sign[:ruler] == planet_name ||
|
|
57
|
+
dignities_for_sign[:exaltation] == planet_name
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.default_dignities
|
|
61
|
+
{
|
|
62
|
+
domicile: false,
|
|
63
|
+
detriment: false,
|
|
64
|
+
exaltation: false,
|
|
65
|
+
fall: false,
|
|
66
|
+
peregrine: true
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|