when_exe 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -33
  3. data/bin/locales.rb +1 -1
  4. data/bin/make_ttl.rb.config +1 -1
  5. data/lib/when_exe.rb +27 -16
  6. data/lib/when_exe/basictypes.rb +772 -771
  7. data/lib/when_exe/calendartypes.rb +1485 -1453
  8. data/lib/when_exe/coordinates.rb +5 -0
  9. data/lib/when_exe/core/compatibility.rb +1 -1
  10. data/lib/when_exe/core/duration.rb +147 -116
  11. data/lib/when_exe/core/extension.rb +499 -497
  12. data/lib/when_exe/ephemeris.rb +1952 -1951
  13. data/lib/when_exe/ephemeris/eclipse.rb +5 -4
  14. data/lib/when_exe/ephemeris/notes.rb +457 -421
  15. data/lib/when_exe/ephemeris/planets.rb +585 -585
  16. data/lib/when_exe/ephemeris/sun.rb +214 -214
  17. data/lib/when_exe/google_api.rb +153 -0
  18. data/lib/when_exe/icalendar.rb +1640 -1632
  19. data/lib/when_exe/inspect.rb +42 -20
  20. data/lib/when_exe/linkeddata.rb +28 -7
  21. data/lib/when_exe/locales/autoload.rb +2 -1
  22. data/lib/when_exe/locales/locale.rb +35 -15
  23. data/lib/when_exe/locales/zh.rb +77 -0
  24. data/lib/when_exe/mini_application.rb +3 -1
  25. data/lib/when_exe/{googlecalendar.rb → obsolete/googlecalendar.rb} +144 -144
  26. data/lib/when_exe/parts/enumerator.rb +498 -486
  27. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  28. data/lib/when_exe/parts/timezone.rb +246 -241
  29. data/lib/when_exe/region/armenian.rb +55 -56
  30. data/lib/when_exe/region/babylonian.rb +406 -405
  31. data/lib/when_exe/region/bahai.rb +107 -106
  32. data/lib/when_exe/region/balinese.rb +624 -622
  33. data/lib/when_exe/region/chinese.rb +1071 -1026
  34. data/lib/when_exe/region/chinese/epochs.rb +28 -28
  35. data/lib/when_exe/region/chinese/notes.rb +219 -0
  36. data/lib/when_exe/region/chinese/twins.rb +803 -803
  37. data/lib/when_exe/region/christian.rb +21 -15
  38. data/lib/when_exe/region/coptic.rb +107 -106
  39. data/lib/when_exe/region/discordian.rb +218 -218
  40. data/lib/when_exe/region/east_asian.rb +1 -1
  41. data/lib/when_exe/region/french.rb +126 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +60 -58
  44. data/lib/when_exe/region/hanke_henry.rb +2 -2
  45. data/lib/when_exe/region/indian.rb +1225 -1222
  46. data/lib/when_exe/region/international_fixed.rb +96 -97
  47. data/lib/when_exe/region/iranian.rb +206 -203
  48. data/lib/when_exe/region/islamic.rb +102 -102
  49. data/lib/when_exe/region/japanese.rb +126 -71
  50. data/lib/when_exe/region/japanese/epochs.rb +426 -426
  51. data/lib/when_exe/region/japanese/notes.rb +101 -81
  52. data/lib/when_exe/region/japanese/residues.rb +1345 -1311
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -112
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +130 -131
  57. data/lib/when_exe/region/m17n.rb +114 -114
  58. data/lib/when_exe/region/martian.rb +258 -258
  59. data/lib/when_exe/region/mayan.rb +11 -8
  60. data/lib/when_exe/region/pax.rb +4 -5
  61. data/lib/when_exe/region/pope.rb +1 -1
  62. data/lib/when_exe/region/positivist.rb +100 -100
  63. data/lib/when_exe/region/residue.rb +162 -162
  64. data/lib/when_exe/region/roman.rb +333 -333
  65. data/lib/when_exe/region/{soviet.rb → russian.rb} +221 -209
  66. data/lib/when_exe/region/shire.rb +222 -223
  67. data/lib/when_exe/region/symmetry.rb +50 -50
  68. data/lib/when_exe/region/thai.rb +336 -336
  69. data/lib/when_exe/region/tibetan.rb +315 -316
  70. data/lib/when_exe/region/tranquility.rb +207 -208
  71. data/lib/when_exe/region/vanishing_leprechaun.rb +3 -1
  72. data/lib/when_exe/region/vietnamese.rb +449 -440
  73. data/lib/when_exe/region/weekdate.rb +80 -80
  74. data/lib/when_exe/region/world.rb +170 -171
  75. data/lib/when_exe/region/world_season.rb +89 -89
  76. data/lib/when_exe/region/yerm.rb +3 -3
  77. data/lib/when_exe/region/zoroastrian.rb +205 -205
  78. data/lib/when_exe/timestandard.rb +708 -707
  79. data/lib/when_exe/tmduration.rb +338 -338
  80. data/lib/when_exe/tmobjects.rb +1356 -1356
  81. data/lib/when_exe/tmposition.rb +66 -31
  82. data/lib/when_exe/version.rb +16 -2
  83. data/test/examples/Residue.m17n +83 -83
  84. data/test/examples/Terms.m17n +2 -2
  85. data/test/test.rb +2 -2
  86. data/test/test/google_api.rb +65 -0
  87. data/test/test/linkeddata.rb +1 -1
  88. data/test/test/{googlecalendar.rb → obsolete/googlecalendar.rb} +194 -194
  89. data/test/test/region/indian.rb +90 -85
  90. data/test/test/region/m17n.rb +7 -7
  91. data/test/test/region/mayan.rb +195 -195
  92. data/test/test/region/residue.rb +153 -153
  93. data/test/test/tmposition.rb +11 -1
  94. data/when_exe.gemspec +2 -2
  95. metadata +95 -8
  96. data/test/test.rb.config +0 -1
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  =begin
3
- Copyright (C) 2014 Takashi SUGA
3
+ Copyright (C) 2014-2015 Takashi SUGA
4
4
 
5
5
  You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
6
  =end
@@ -52,13 +52,14 @@ class When::Coordinates::Spatial
52
52
  #
53
53
  # @param [When::TM::TemporalPosition] date
54
54
  # @param [Range<When::TM::TemporalPosition>] date
55
- # @note Rangeの場合午前6時より前は前日扱い
55
+ # @note Rangeの場合夜半より翌日に向けmargin経過時点より前は前日扱い
56
+ # @param [When::TM::Duration] margin
56
57
  # @param [Block] block
57
58
  #
58
59
  # @return [Array<String, Numeric, Array<Array<Numeric or When::TM::TemporalPosition, String>>>] 食の情報(のArray(dateがRangeの場合))
59
60
  # @see When::Coordinates::Spatial#eclipse_info
60
61
  #
61
- def lunar_eclipse(date, &block)
62
+ def lunar_eclipse(date, margin=When::PT6H, &block)
62
63
  if date.kind_of?(Range)
63
64
  last = date.last.to_i
64
65
  last -= 1 if date.exclude_end?
@@ -74,7 +75,7 @@ class When::Coordinates::Spatial
74
75
  data = EclipseRange.include?(time % EclipseHalfYear) ?
75
76
  eclipse_info(@mean._to_seed_type(time, date), When.Resource('_ep:Earth'), When.Resource('_ep:Moon'),
76
77
  When.Resource('_ep:Shadow'), [self, When.Resource('_ep:Moon')]) : nil
77
- @ecls[[cn,clock.to_s]] = data ? [(data[2][data[2].size / 2][0]-When::PT6H).to_i, data] : nil
78
+ @ecls[[cn,clock.to_s]] = data ? [(data[2][data[2].size / 2][0]-margin).to_i, data] : nil
78
79
  end
79
80
  key, info = @ecls[[cn,clock.to_s]]
80
81
  return info unless first
@@ -1,421 +1,457 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2014 Takashi SUGA
4
-
5
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
- =end
7
-
8
- #
9
- # Ephemeris を用いる暦注
10
- #
11
- class When::CalendarNote
12
- #
13
- # 太陽と月の位置によるイベント
14
- #
15
- class LuniSolarPositions < self
16
-
17
- # 座標の分子
18
- #
19
- # @return [Numeric]
20
- #
21
- attr_reader :num
22
-
23
- # 座標の分母
24
- #
25
- # @return [Numeric]
26
- #
27
- attr_reader :den
28
-
29
- # 計算アルゴリズム
30
- #
31
- # @return [When::Ephemeris::Formula]
32
- #
33
- attr_reader :formula
34
-
35
- # enumerator の周期
36
- #
37
- # @return [Numeric]
38
- #
39
- attr_reader :delta
40
-
41
- # 没滅計算用の補正
42
- #
43
- # @return [Numeric]
44
- #
45
- attr_reader :margin
46
-
47
- # イベントの日時
48
- #
49
- # @param [When::TM::TemporalPosition] date イベントを探す基準とする日時
50
- # @param [Array<Numeric>] parameter 座標の分子と分母( num, den)
51
- #
52
- # num 座標の分子 (デフォルト @num)
53
- #
54
- # den 座標の分母 (デフォルト @den)
55
- #
56
- # @param [String] parameter 座標の分子と分母("#{ num }/#{ den }" の形式)
57
- # @param [Integer] precision 取得したい時間位置の分解能(デフォルト date の分解能)
58
- #
59
- # @return [When::TM::CalDate] date またはその直後のイベントの日時
60
- #
61
- def event_eval(date, parameter=@event, precision=nil)
62
- date = When.when?(date) unless date.kind_of?(When::TimeValue)
63
- precision ||= date.precision
64
- num, den = parameter.kind_of?(String) ? (parameter[/\d.*\z/]||'').split(/\//, 2) : parameter
65
- num = (num || @num).to_f
66
- den = (den || @den).to_f
67
- date = date.floor(precision) if precision < date.precision
68
- options = date._attr
69
- is_date_and_time = options.key?(:clock) || precision > When::DAY
70
- options[:precision] = precision
71
- options[:clock] ||= date.frame.time_basis || When::TM::Clock.local_time
72
- sdn = _the_date(date, num, den)
73
- time = When::TM::JulianDate._d_to_t(sdn)
74
- if @formula.is_dynamical
75
- time = date.time_standard.from_dynamical_time(time)
76
- time += options[:clock].universal_time(sdn.round) if options[:clock].kind_of?(When::CalendarTypes::LocalTime)
77
- end
78
- event = date.frame.jul_trans(When::TM::JulianDate.universal_time(time), options)
79
- is_date_and_time ? event : event.to_cal_date
80
- end
81
-
82
- # the event date
83
- def _the_date(date, num, den)
84
- quot, mod = (@formula.time_to_cn(date)*30.0).divmod(den)
85
- cycle = quot * den + num
86
- cycle += den if mod > (num % den)
87
- @formula.cn_to_time(cycle/30.0)
88
- end
89
- private :_the_date
90
-
91
- # 日付に対応する座標
92
- #
93
- # @param [When::TM::TemporalPosition] date 日付
94
- # @param [Numeric] delta 周期の補正(土用の時刻の補正に使用,デフォルト 0)
95
- #
96
- # @return [Array<Integer>] Array< Integer, 0 or 1 or 2 >
97
- #
98
- # [Integer] 対応する座標
99
- #
100
- # [0 or 1 or 2] 座標の進み(0 なら 没, 2 なら滅)
101
- #
102
- def position(date, delta=0)
103
- date = date.floor
104
- p0, p1 = [date, date.succ].map {|d| (30.0 * @formula.time_to_cn(d) - @margin + delta).floor}
105
- [p1 % @den, p1 - p0]
106
- end
107
-
108
- #
109
- # イベントの標準的な間隔を返す
110
- #
111
- # @param [String] parameter 座標の分子と分母("#{ num }/#{ den }" の形式)
112
- #
113
- # @return [When::TM::IntervalLength]
114
- #
115
- # @private
116
- def event_delta(parameter=nil)
117
- return @delta unless parameter
118
- num, den = parameter.kind_of?(String) ? parameter.split(/\//, 2) : parameter
119
- When::TM::IntervalLength.new([(den || @den).to_f,1].max*0.9, 'day')
120
- end
121
-
122
- #
123
- # イベント日付(時刻付)
124
- #
125
- # @private
126
- def event_time(date, event_name, event)
127
- etime = term(date - When.Duration('P3D'), event, When::SYSTEM)
128
- if formula.respond_to?(:year_length) && formula.denominator && formula.denominator < 100000
129
- fraction = etime.clk_time.local_time
130
- fraction += When::TM::Duration::DAY * (etime.to_i - date.to_i)
131
- fraction = (fraction / When::TM::Duration::DAY * formula.denominator * 1000 + 0.5).floor / 1000.0
132
- fraction = fraction.to_i if fraction == fraction.to_i
133
- event_name + "(#{fraction}/#{formula.denominator})"
134
- else
135
- etime.events = [event_name]
136
- etime
137
- end
138
- end
139
- end
140
-
141
- #
142
- # 二十四節気
143
- #
144
- class SolarTerms < LuniSolarPositions
145
-
146
- # 二十四節気のための event の別名
147
- #
148
- # @return [When::TM::CalDate] date またはその直後のイベントの日時
149
- alias :term :event_eval
150
-
151
- private
152
-
153
- # 二十四節気のための event_delta の別名
154
- alias :term_delta :event_delta
155
-
156
- # オブジェクトの正規化
157
- # num - 太陽黄経/度の分子 (デフォルト 0 - 春分)
158
- # den - 太陽黄経/度の分母 (デフォルト 360 - 1年)
159
- # formula - 計算アルゴリズム(デフォルト '_ep:Formula?formula=12S')
160
- # delta - enumerator の周期 (デフォルト (den/360)年)
161
- # margin - 没滅計算用の補正 (デフォルト 1E-8)
162
- def _normalize(args=[], options={})
163
- num, den, formula, delta, margin = args
164
- @event ||= 'term'
165
- @num = (num || @num || 0).to_f
166
- @den = (den || @den || 360).to_f
167
- @formula = When.Resource(formula || @formula ||'Formula?formula=12S', '_ep:')
168
- @delta = When.Duration(delta || @delta || When::TM::IntervalLength.new(@den/360, 'year'))
169
- @margin = (margin || @margin || 1E-8).to_f
170
- super
171
- end
172
- end
173
-
174
- #
175
- # 冬至を定気で計算し、その他の二十四節気を前後の冬至の日時を時間で等分して求める
176
- #
177
- class SolarTermsRevised < SolarTerms
178
-
179
- # the event date
180
- def _the_date(date, num, den)
181
- quot, mod = @formula.time_to_cn(date).divmod(12)
182
- quot += 1 if mod >= 9
183
- range = [12*quot-3, 12*quot+9].map {|cn| [cn*30, @formula.cn_to_time(cn)]}
184
- time = @formula.is_dynamical ? +date : date.to_f
185
- now = [range[0][0] + (range[1][0] - range[0][0]) / (range[1][1] - range[0][1]) * (time - range[0][1]), time]
186
-
187
- quot, mod = now[0].divmod(den)
188
- cycle = quot * den + num
189
- cycle += den if mod > (num % den)
190
- range[0][1] + (range[1][1] - range[0][1]) / (range[1][0] - range[0][0]) * (cycle - range[0][0])
191
- end
192
- end
193
-
194
- #
195
- # 月の位相
196
- #
197
- class LunarPhases < LuniSolarPositions
198
-
199
- # 月の位相のための event の別名
200
- #
201
- # @return [When::TM::CalDate] date またはその直後のイベントの日時
202
- alias :phase :event_eval
203
-
204
- private
205
-
206
- # 月の位相のための event_delta の別名
207
- alias :phase_delta :event_delta
208
-
209
- # オブジェクトの正規化
210
- # num - 月の位相/12度の分子 (デフォルト 0 - 朔)
211
- # den - 月の位相/12度の分母 (デフォルト 30 - 1月)
212
- # formula - 計算アルゴリズム(デフォルト '_ep:Formula?formula=1L')
213
- # delta - enumerator の周期 (デフォルト (den/30)月)
214
- # margin - 没滅計算用の補正 (デフォルト 1E-8)
215
- def _normalize(args=[], options={})
216
- num, den, formula, delta, margin = args
217
- @event ||= 'phase'
218
- @num = (num || @num || 0).to_f
219
- @den = (den || @den || 30).to_f
220
- @formula = When.Resource(formula || @formula ||'Formula?formula=1L', '_ep:')
221
- @delta = When.Duration(delta || @delta || When::TM::IntervalLength.new(@den/30, 'month'))
222
- @margin = (margin || @margin || 1E-8).to_f
223
- super
224
- end
225
- end
226
-
227
- #
228
- # 天体暦の暦注
229
- #
230
- class Ephemeris < self
231
-
232
- Notes = [When::BasicTypes::M17n, [
233
- "locale:[=en:, ja=ja:, alias=ja:]",
234
- "names:[Ephemeris]",
235
-
236
- # 年の暦注 ----------------------------
237
- [When::BasicTypes::M17n,
238
- "names:[year]"
239
- ],
240
-
241
- # 月の暦注 ----------------------------
242
- [When::BasicTypes::M17n,
243
- "names:[month]",
244
- [When::BasicTypes::M17n,
245
- "names:[Month]"
246
- ]
247
- ],
248
-
249
- # 日の暦注 ----------------------------
250
- [When::BasicTypes::M17n,
251
- "names:[day]",
252
- "[Sunrise, 日の出 ]", # 日の出
253
- "[Sunset, 日の入り ]", # 日の入り
254
- [When::Coordinates::Residue,
255
- "label:[Moon_Age=, 正午月齢=ja:%%<月齢>]",
256
- "divisor:60",
257
- "format:[%s(%4.1f)=]"
258
- ],
259
- "[Moonrise, 月の出 ]", # 月の出
260
- "[Moonset=, 月の入り ]", # 月の入り
261
- [When::BasicTypes::M17n,
262
- "names:[Tide, 潮汐]", # 満潮干潮日時
263
- "[High_Tide=en:Tide, 満潮=ja:%%<潮汐>]",
264
- "[Low_Tide=en:Tide, 干潮=ja:%%<潮汐>]"
265
- ]
266
- ]
267
- ]]
268
-
269
- #
270
- # 日の出
271
- #
272
- # @param [When::TM::TemporalPosition] date
273
- # @param [Hash] options dummy
274
- #
275
- # @return [When::TM::TemporalPosition] 日の出の時刻をイベント時刻とする
276
- #
277
- def sunrise(date, options={})
278
- event = formula(date.location.iri).sunrise(date)
279
- event.events = [@root['Sunrise']]
280
- event
281
- rescue
282
- nil
283
- end
284
-
285
- #
286
- # 日の入り
287
- #
288
- # @param [When::TM::TemporalPosition] date
289
- # @param [Hash] options dummy
290
- #
291
- # @return [When::TM::TemporalPosition] 日の入りの時刻をイベント時刻とする
292
- #
293
- def sunset(date, options={})
294
- event = formula(date.location.iri).sunset(date)
295
- event.events = [@root['Sunset']]
296
- event
297
- rescue
298
- nil
299
- end
300
-
301
- #
302
- # 正午月齢
303
- #
304
- # @param [When::TM::TemporalPosition] date
305
- # @param [Hash] options dummy
306
- #
307
- # @return [Numeric] 正午における朔からの経過日数
308
- #
309
- def moon_age(date, options={})
310
- @phase ||= When.CalendarNote('LunarPhases')
311
- noon = date.floor(When::DAY,When::SYSTEM) + 0.5
312
- @root['Moon_Age'][noon.to_f - @phase.phase(noon, [-30.0,30.0]).to_f]
313
- end
314
-
315
- #
316
- # 月の出
317
- #
318
- # @param [When::TM::TemporalPosition] date
319
- # @param [Hash] options dummy
320
- #
321
- # @return [When::TM::TemporalPosition] 月の出の時刻をイベント時刻とする
322
- #
323
- def moonrise(date, options={})
324
- event = formula(date.location.iri).moonrise(date)
325
- event.events = [@root['Moonrise']]
326
- event
327
- rescue
328
- nil
329
- end
330
-
331
- #
332
- # 月の入り
333
- #
334
- # @param [When::TM::TemporalPosition] date
335
- # @param [Hash] options dummy
336
- #
337
- # @return [When::TM::TemporalPosition] 月の入りの時刻をイベント時刻とする
338
- #
339
- def moonset(date, options={})
340
- event = formula(date.location.iri).moonset(date)
341
- event.events = [@root['Moonset']]
342
- event
343
- rescue
344
- nil
345
- end
346
-
347
- # 干潮・満潮の日時
348
- #
349
- # @param [When::TM::TemporalPosition] date
350
- # @param [Hash] options 以下の通り
351
- # @option options [String] tide 潮汐計算方式 'Horizontal' - 地平高度基準, 'Equatorial' - 子午線通過基準(デフォルト)
352
- #
353
- # @return [Array<Array<Integer, When::TM::TemporalPosotion>>] 干潮・満潮の日時の Array
354
- #
355
- # [Integer] +1:満潮, -1:干潮
356
- #
357
- # [When::TM::TemporalPosotion] 干潮・満潮の日時
358
- #
359
- #
360
- def tide(date, options={})
361
- return nil unless @interval
362
- @target ||= When.Resource('_ep:Moon')
363
- events = @root['Tide']
364
- form = formula(date.location.iri)
365
- type = options[:tide] =~ /\Ahorizon/i ? nil : 0
366
-
367
- now = +date
368
- high_tides = []
369
- 5.times do |i|
370
- high_tide = form.day_event(now + i - 2, type, @target) + @interval
371
- high_tides << high_tide if high_tides.size == 0 || high_tide > high_tides[-1] + 0.5
372
- end
373
- tides = []
374
-
375
- (high_tides.size-1).times do |i|
376
- tides << [0, high_tides[i]]
377
- tides << [1, 0.75*high_tides[i] + 0.25*high_tides[i+1]]
378
- tides << [0, 0.50*high_tides[i] + 0.50*high_tides[i+1]]
379
- tides << [1, 0.25*high_tides[i] + 0.75*high_tides[i+1]]
380
- end
381
- tides << [0, high_tides[-1]]
382
-
383
- today = +date.floor(When::DAY)...+date.ceil(When::DAY)
384
- seed = date._attr
385
- seed[:clock] ||= When::TM::Clock.local_time
386
- tides.select {|x| today.include?(x[1])}.map {|x|
387
- d = form._to_seed_type(x[1], seed)
388
- d.events = [events[x[0]]]
389
- d
390
- }
391
- rescue
392
- nil
393
- end
394
-
395
- private
396
-
397
- # オブジェクトの正規化
398
- # long - 計算に用いる経度 / 度
399
- # lat - 計算に用いる緯度 / 度
400
- # alt - 計算に用いる高度 / m
401
- # interval - 高潮間隔(月の子午線通過から満潮までの時間) / 時間
402
- def _normalize(args=[], options={})
403
- if @location
404
- @location = When.Resource(@location)
405
- @formula = When.Resource(@formula || "Formula?location=(#{@location.iri})", '_ep:')
406
- else
407
- @formula = {}
408
- end
409
- @interval = @interval.sub('@','.').to_f / 24 if @interval
410
- @root = When.CalendarNote('Ephemeris/Notes::day')
411
- @prime ||= [%w(Month), %w(Sunrise Sunset Moon_Age)]
412
- super
413
- end
414
-
415
- # 計算に用いる Ephemeris
416
- def formula(location)
417
- return @formula unless @formula.kind_of?(Hash)
418
- @formula[location] ||= When.Resource("Formula?location=(#{location})", '_ep:')
419
- end
420
- end
421
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2015 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ #
9
+ # Ephemeris を用いる暦注
10
+ #
11
+ class When::CalendarNote
12
+ #
13
+ # 太陽と月の位置によるイベント
14
+ #
15
+ class LuniSolarPositions < self
16
+
17
+ # 座標の分子
18
+ #
19
+ # @return [Numeric]
20
+ #
21
+ attr_reader :num
22
+
23
+ # 座標の分母
24
+ #
25
+ # @return [Numeric]
26
+ #
27
+ attr_reader :den
28
+
29
+ # 計算アルゴリズム
30
+ #
31
+ # @return [When::Ephemeris::Formula]
32
+ #
33
+ attr_reader :formula
34
+
35
+ # enumerator の周期
36
+ #
37
+ # @return [Numeric]
38
+ #
39
+ attr_reader :delta
40
+
41
+ # 没滅計算用の補正
42
+ #
43
+ # @return [Numeric]
44
+ #
45
+ attr_reader :margin
46
+
47
+ # イベントの日時
48
+ #
49
+ # @param [When::TM::TemporalPosition] date イベントを探す基準とする日時
50
+ # @param [Array<Numeric>] parameter 座標の分子と分母( num, den)
51
+ #
52
+ # num 座標の分子 (デフォルト @num)
53
+ #
54
+ # den 座標の分母 (デフォルト @den)
55
+ #
56
+ # @param [String] parameter 座標の分子と分母("#{ num }/#{ den }" の形式)
57
+ # @param [Integer] precision 取得したい時間位置の分解能(デフォルト date の分解能)
58
+ #
59
+ # @return [When::TM::CalDate] date またはその直後のイベントの日時
60
+ #
61
+ def event_eval(date, parameter=@event, precision=nil)
62
+ date = When.when?(date) unless date.kind_of?(When::TimeValue)
63
+ precision ||= date.precision
64
+ num, den = parameter.kind_of?(String) ? (parameter[/\d.*\z/]||'').split(/\//, 2) : parameter
65
+ num = (num || @num).to_f
66
+ den = (den || @den).to_f
67
+ date = date.floor(precision) if precision < date.precision
68
+ options = date._attr
69
+ is_date_and_time = options.key?(:clock) || precision > When::DAY
70
+ options[:precision] = precision
71
+ options[:clock] ||= date.frame.time_basis || When::TM::Clock.local_time
72
+ sdn = _the_date(date, num, den)
73
+ time = When::TM::JulianDate._d_to_t(sdn)
74
+ if @formula.is_dynamical
75
+ time = date.time_standard.from_dynamical_time(time)
76
+ time += options[:clock].universal_time(sdn.round) if options[:clock].kind_of?(When::CalendarTypes::LocalTime)
77
+ end
78
+ event = date.frame.jul_trans(When::TM::JulianDate.universal_time(time), options)
79
+ is_date_and_time ? event : event.to_cal_date
80
+ end
81
+
82
+ # the event date
83
+ def _the_date(date, num, den)
84
+ quot, mod = (@formula.time_to_cn(date)*30.0).divmod(den)
85
+ cycle = quot * den + num
86
+ cycle += den if mod > (num % den)
87
+ @formula.cn_to_time(cycle/30.0)
88
+ end
89
+ private :_the_date
90
+
91
+ # 日付に対応する座標
92
+ #
93
+ # @param [When::TM::TemporalPosition] date 日付
94
+ # @param [Numeric] delta 周期の補正(土用の時刻の補正に使用,デフォルト 0)
95
+ #
96
+ # @return [Array<Integer>] Array< Integer, 0 or 1 or 2 >
97
+ #
98
+ # [Integer] 対応する座標
99
+ #
100
+ # [0 or 1 or 2] 座標の進み(0 なら 没, 2 なら滅)
101
+ #
102
+ def position(date, delta=0)
103
+ date = date.floor
104
+ p0, p1 = [date, date.succ].map {|d| (30.0 * @formula.time_to_cn(d) - @margin + delta).floor}
105
+ [p1 % @den, p1 - p0]
106
+ end
107
+
108
+ #
109
+ # イベントの標準的な間隔を返す
110
+ #
111
+ # @param [String] parameter 座標の分子と分母("#{ num }/#{ den }" の形式)
112
+ #
113
+ # @return [When::TM::IntervalLength]
114
+ #
115
+ # @private
116
+ def event_delta(parameter=nil)
117
+ return @delta unless parameter
118
+ num, den = parameter.kind_of?(String) ? parameter.split(/\//, 2) : parameter
119
+ When::TM::IntervalLength.new([(den || @den).to_f,1].max*0.9, 'day')
120
+ end
121
+
122
+ #
123
+ # イベント日付(時刻付)
124
+ #
125
+ # @private
126
+ def event_time(date, event_name, event)
127
+ etime = term(date - When.Duration('P3D'), event, When::SYSTEM)
128
+ if formula.respond_to?(:year_length) && formula.denominator && formula.denominator < 100000
129
+ fraction = etime.clk_time.local_time
130
+ fraction += When::TM::Duration::DAY * (etime.to_i - date.to_i)
131
+ fraction = (fraction / When::TM::Duration::DAY * formula.denominator * 1000 + 0.5).floor / 1000.0
132
+ fraction = fraction.to_i if fraction == fraction.to_i
133
+ event_name + "(#{fraction}/#{formula.denominator})"
134
+ else
135
+ etime.events = [event_name]
136
+ etime
137
+ end
138
+ end
139
+ end
140
+
141
+ #
142
+ # 二十四節気
143
+ #
144
+ class SolarTerms < LuniSolarPositions
145
+
146
+ # 二十四節気のための event の別名
147
+ #
148
+ # @return [When::TM::CalDate] date またはその直後のイベントの日時
149
+ alias :term :event_eval
150
+
151
+ private
152
+
153
+ # 二十四節気のための event_delta の別名
154
+ alias :term_delta :event_delta
155
+
156
+ # オブジェクトの正規化
157
+ # num - 太陽黄経/度の分子 (デフォルト 0 - 春分)
158
+ # den - 太陽黄経/度の分母 (デフォルト 360 - 1年)
159
+ # formula - 計算アルゴリズム(デフォルト '_ep:Formula?formula=12S')
160
+ # delta - enumerator の周期 (デフォルト (den/360)年)
161
+ # margin - 没滅計算用の補正 (デフォルト 1E-8)
162
+ def _normalize(args=[], options={})
163
+ num, den, formula, delta, margin = args
164
+ @event ||= 'term'
165
+ @num = (num || @num || 0).to_f
166
+ @den = (den || @den || 360).to_f
167
+ @formula = When.Resource(formula || @formula ||'Formula?formula=12S', '_ep:')
168
+ @delta = When.Duration(delta || @delta || When::TM::IntervalLength.new(@den/360, 'year'))
169
+ @margin = (margin || @margin || 1E-8).to_f
170
+ super
171
+ end
172
+ end
173
+
174
+ #
175
+ # 冬至を定気で計算し、その他の二十四節気を前後の冬至の日時を時間で等分して求める
176
+ #
177
+ class SolarTermsRevised < SolarTerms
178
+
179
+ # the event date
180
+ def _the_date(date, num, den)
181
+ quot, mod = @formula.time_to_cn(date).divmod(12)
182
+ quot += 1 if mod >= 9
183
+ range = [12*quot-3, 12*quot+9].map {|cn| [cn*30, @formula.cn_to_time(cn)]}
184
+ time = @formula.is_dynamical ? +date : date.to_f
185
+ now = [range[0][0] + (range[1][0] - range[0][0]) / (range[1][1] - range[0][1]) * (time - range[0][1]), time]
186
+
187
+ quot, mod = now[0].divmod(den)
188
+ cycle = quot * den + num
189
+ cycle += den if mod > (num % den)
190
+ range[0][1] + (range[1][1] - range[0][1]) / (range[1][0] - range[0][0]) * (cycle - range[0][0])
191
+ end
192
+ end
193
+
194
+ #
195
+ # 月の位相
196
+ #
197
+ class LunarPhases < LuniSolarPositions
198
+
199
+ # 月の位相のための event の別名
200
+ #
201
+ # @return [When::TM::CalDate] date またはその直後のイベントの日時
202
+ alias :phase :event_eval
203
+
204
+ private
205
+
206
+ # 月の位相のための event_delta の別名
207
+ alias :phase_delta :event_delta
208
+
209
+ # オブジェクトの正規化
210
+ # num - 月の位相/12度の分子 (デフォルト 0 - 朔)
211
+ # den - 月の位相/12度の分母 (デフォルト 30 - 1月)
212
+ # formula - 計算アルゴリズム(デフォルト '_ep:Formula?formula=1L')
213
+ # delta - enumerator の周期 (デフォルト (den/30)月)
214
+ # margin - 没滅計算用の補正 (デフォルト 1E-8)
215
+ def _normalize(args=[], options={})
216
+ num, den, formula, delta, margin = args
217
+ @event ||= 'phase'
218
+ @num = (num || @num || 0).to_f
219
+ @den = (den || @den || 30).to_f
220
+ @formula = When.Resource(formula || @formula ||'Formula?formula=1L', '_ep:')
221
+ @delta = When.Duration(delta || @delta || When::TM::IntervalLength.new(@den/30, 'month'))
222
+ @margin = (margin || @margin || 1E-8).to_f
223
+ super
224
+ end
225
+ end
226
+
227
+ #
228
+ # 天体暦の暦注
229
+ #
230
+ class Ephemeris < self
231
+
232
+ Notes = [When::BasicTypes::M17n, [
233
+ "locale:[=en:, ja=ja:, alias=ja:]",
234
+ "names:[Ephemeris]",
235
+
236
+ # 年の暦注 ----------------------------
237
+ [When::BasicTypes::M17n,
238
+ "names:[year, 年]"
239
+ ],
240
+
241
+ # 月の暦注 ----------------------------
242
+ [When::BasicTypes::M17n,
243
+ "names:[month, 月]",
244
+ [When::BasicTypes::M17n,
245
+ "names:[Month]"
246
+ ]
247
+ ],
248
+
249
+ # 日の暦注 ----------------------------
250
+ [When::BasicTypes::M17n,
251
+ "names:[day, 日]",
252
+ "[Sunrise, 日の出 ]", # 日の出
253
+ "[Sunset, 日の入り ]", # 日の入り
254
+ [When::Coordinates::Residue,
255
+ "label:[Moon_Age=, 正午月齢=ja:%%<月齢>]",
256
+ "divisor:60",
257
+ "format:[%s(%4.1f)=]"
258
+ ],
259
+ "[Moonrise, 月の出 ]", # 月の出
260
+ "[Moonset=, 月の入り ]", # 月の入り
261
+ [When::BasicTypes::M17n,
262
+ "names:[Tide, 潮汐]", # 満潮干潮日時
263
+ "[High_Tide=en:Tide, 満潮=ja:%%<潮汐>]",
264
+ "[Low_Tide=en:Tide, 干潮=ja:%%<潮汐>]"
265
+ ],
266
+ "[SolarTerm=, 二十四節気 ]", # 二十四節気
267
+ "[LunarPhase=, 月の位相 ]" # 月の位相
268
+ ]
269
+ ]]
270
+
271
+ #
272
+ # 日の出
273
+ #
274
+ # @param [When::TM::TemporalPosition] date
275
+ # @param [Hash] options dummy
276
+ #
277
+ # @return [When::TM::TemporalPosition] 日の出の時刻をイベント時刻とする
278
+ #
279
+ def sunrise(date, options={})
280
+ event = formula(date.location.iri).sunrise(date)
281
+ event.events = [@root['Sunrise']]
282
+ event
283
+ rescue
284
+ nil
285
+ end
286
+
287
+ #
288
+ # 日の入り
289
+ #
290
+ # @param [When::TM::TemporalPosition] date
291
+ # @param [Hash] options dummy
292
+ #
293
+ # @return [When::TM::TemporalPosition] 日の入りの時刻をイベント時刻とする
294
+ #
295
+ def sunset(date, options={})
296
+ event = formula(date.location.iri).sunset(date)
297
+ event.events = [@root['Sunset']]
298
+ event
299
+ rescue
300
+ nil
301
+ end
302
+
303
+ #
304
+ # 正午月齢
305
+ #
306
+ # @param [When::TM::TemporalPosition] date
307
+ # @param [Hash] options dummy
308
+ #
309
+ # @return [Numeric] 正午における朔からの経過日数
310
+ #
311
+ def moon_age(date, options={})
312
+ @phase ||= When.CalendarNote('LunarPhases')
313
+ noon = date.floor(When::DAY,When::SYSTEM)
314
+ noon += 0.5 if noon.kind_of?(When::TM::DateAndTime)
315
+ @root['Moon_Age'][noon.to_f - @phase.phase(noon, [-30.0,30.0]).to_f]
316
+ end
317
+
318
+ #
319
+ # 月の出
320
+ #
321
+ # @param [When::TM::TemporalPosition] date
322
+ # @param [Hash] options dummy
323
+ #
324
+ # @return [When::TM::TemporalPosition] 月の出の時刻をイベント時刻とする
325
+ #
326
+ def moonrise(date, options={})
327
+ event = formula(date.location.iri).moonrise(date)
328
+ event.events = [@root['Moonrise']]
329
+ event
330
+ rescue
331
+ nil
332
+ end
333
+
334
+ #
335
+ # 月の入り
336
+ #
337
+ # @param [When::TM::TemporalPosition] date
338
+ # @param [Hash] options dummy
339
+ #
340
+ # @return [When::TM::TemporalPosition] 月の入りの時刻をイベント時刻とする
341
+ #
342
+ def moonset(date, options={})
343
+ event = formula(date.location.iri).moonset(date)
344
+ event.events = [@root['Moonset']]
345
+ event
346
+ rescue
347
+ nil
348
+ end
349
+
350
+ #
351
+ # 干潮・満潮の日時
352
+ #
353
+ # @param [When::TM::TemporalPosition] date
354
+ # @param [Hash] options 以下の通り
355
+ # @option options [String] tide 潮汐計算方式 'Horizontal' - 地平高度基準, 'Equatorial' - 子午線通過基準(デフォルト)
356
+ #
357
+ # @return [Array<Array<Integer, When::TM::TemporalPosotion>>] 干潮・満潮の日時の Array
358
+ #
359
+ # [Integer] +1:満潮, -1:干潮
360
+ #
361
+ # [When::TM::TemporalPosotion] 干潮・満潮の日時
362
+ #
363
+ def tide(date, options={})
364
+ return nil unless @interval
365
+ @target ||= When.Resource('_ep:Moon')
366
+ events = @root['Tide']
367
+ form = formula(date.location.iri)
368
+ type = options[:tide] =~ /\Ahorizon/i ? nil : 0
369
+
370
+ now = +date
371
+ high_tides = []
372
+ 5.times do |i|
373
+ high_tide = form.day_event(now + i - 2, type, @target) + @interval
374
+ high_tides << high_tide if high_tides.size == 0 || high_tide > high_tides[-1] + 0.5
375
+ end
376
+ tides = []
377
+
378
+ (high_tides.size-1).times do |i|
379
+ tides << [0, high_tides[i]]
380
+ tides << [1, 0.75*high_tides[i] + 0.25*high_tides[i+1]]
381
+ tides << [0, 0.50*high_tides[i] + 0.50*high_tides[i+1]]
382
+ tides << [1, 0.25*high_tides[i] + 0.75*high_tides[i+1]]
383
+ end
384
+ tides << [0, high_tides[-1]]
385
+
386
+ today = +date.floor(When::DAY)...+date.ceil(When::DAY)
387
+ seed = date._attr
388
+ seed[:clock] ||= When::TM::Clock.local_time
389
+ tides.select {|x| today.include?(x[1])}.map {|x|
390
+ d = form._to_seed_type(x[1], seed)
391
+ d.events = [events[x[0]]]
392
+ d
393
+ }
394
+ rescue
395
+ nil
396
+ end
397
+
398
+ #
399
+ # 太陽黄経
400
+ #
401
+ # @param [When::TM::TemporalPosition] date
402
+ # @param [Hash] options dummy
403
+ #
404
+ # @return [When::Coordinates::Residue] 二十四節気
405
+ #
406
+ def solarterm(date, options={})
407
+ @term ||= When.CalendarNote('SolarTerms')
408
+ longitude, motsu = @term.position(date)
409
+ return nil if motsu == 0
410
+ div, mod = longitude.divmod(15)
411
+ return nil unless mod == 0
412
+ When.Resource('_co:Common::二十四節気::*')[div]
413
+ end
414
+
415
+ #
416
+ # 月の位相
417
+ #
418
+ # @param [When::TM::TemporalPosition] date
419
+ # @param [Hash] options dummy
420
+ #
421
+ # @return [When::Coordinates::Residue] 月の位相
422
+ #
423
+ def lunarphase(date, options={})
424
+ @phase ||= When.CalendarNote('LunarPhases')
425
+ longitude, metsu = @phase.position(date)
426
+ div, mod = longitude.divmod(90)
427
+ return nil unless mod == 0
428
+ When.Resource('_co:Common::月相::*')[div]
429
+ end
430
+
431
+ private
432
+
433
+ # オブジェクトの正規化
434
+ # long - 計算に用いる経度 / 度
435
+ # lat - 計算に用いる緯度 / 度
436
+ # alt - 計算に用いる高度 / m
437
+ # interval - 高潮間隔(月の子午線通過から満潮までの時間) / 時間
438
+ def _normalize(args=[], options={})
439
+ if @location
440
+ @location = When.Resource(@location)
441
+ @formula = When.Resource(@formula || "Formula?location=(#{@location.iri})", '_ep:')
442
+ else
443
+ @formula = {}
444
+ end
445
+ @interval = @interval.sub('@','.').to_f / 24 if @interval
446
+ @root = When.CalendarNote('Ephemeris/Notes::day')
447
+ @prime ||= [%w(Month), %w(Sunrise Sunset Moon_Age)]
448
+ super
449
+ end
450
+
451
+ # 計算に用いる Ephemeris
452
+ def formula(location)
453
+ return @formula unless @formula.kind_of?(Hash)
454
+ @formula[location] ||= When.Resource("Formula?location=(#{location})", '_ep:')
455
+ end
456
+ end
457
+ end