when_exe 0.4.1 → 0.4.2

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.
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