Module: Unitsml::Utility

Defined in:
lib/unitsml/utility.rb

Constant Summary collapse

U2D =

Unit to dimension

{
  "m" => { dimension: "Length", order: 1, symbol: "L" },
  "g" => { dimension: "Mass", order: 2, symbol: "M" },
  "kg" => { dimension: "Mass", order: 2, symbol: "M" },
  "s" => { dimension: "Time", order: 3, symbol: "T" },
  "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
  "K" => { dimension: "ThermodynamicTemperature", order: 5,
           symbol: "Theta" },
  "degK" => { dimension: "ThermodynamicTemperature", order: 5,
              symbol: "Theta" },
  "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
  "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
  "deg" => { dimension: "PlaneAngle", order: 8, symbol: "phi" },
}.freeze
DIM2D =

Dimesion for dim_(dimesion) input

{
  "dim_L" => U2D["m"],
  "dim_M" => U2D["g"],
  "dim_T" => U2D["s"],
  "dim_I" => U2D["A"],
  "dim_Theta" => U2D["K"],
  "dim_N" => U2D["mol"],
  "dim_J" => U2D["cd"],
  "dim_phi" => U2D["deg"],
}.freeze
DIMS_VECTOR =
%w[
  ThermodynamicTemperature
  AmountOfSubstance
  LuminousIntensity
  ElectricCurrent
  PlaneAngle
  Length
  Mass
  Time
].freeze
UNKNOWN =
"unknown"
PREFIX_SYMBOL_METHODS =
{
  ASCII: :to_asciimath,
  unicode: :to_unicode,
  LaTeX: :to_latex,
  HTML: :to_html,
}.freeze

Class Method Summary collapse

Class Method Details

.combine_prefixes(p1, p2) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/unitsml/utility.rb', line 147

def combine_prefixes(p1, p2)
  a1 = PrefixAdapter.wrap(p1)
  a2 = PrefixAdapter.wrap(p2)
  return nil if a1.null? && a2.null?
  return a1.symbolid if a2.null?
  return a2.symbolid if a1.null?
  return UNKNOWN if a1.base != a2.base

  Unitsdb.prefixes_array.each do |prefix_name|
    candidate = PrefixAdapter.from_name(prefix_name)
    return candidate.raw if candidate.base == a1.base &&
      candidate.power == a1.power + a2.power
  end

  UNKNOWN
end

.decompose_unit(u) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/unitsml/utility.rb', line 104

def decompose_unit(u)
  if u&.unit_name == "g" || Lutaml::Model::Utils.snake_case(u.system_type) == "si_base"
    { unit: u, prefix: u&.prefix }
  elsif u.si_derived_bases.nil? || u.si_derived_bases.empty?
    { unit: Unit.new(UNKNOWN) }
  else
    u.si_derived_bases.each_with_object([]) do |k, object|
      prefix = if k.prefix_reference.nil?
                 u.prefix
               else
                 combine_prefixes(k.prefix_reference, u.prefix)
               end
      unit_name = Unitsdb.units.find_by_id(k.unit_reference.id).symbols.first.id
      exponent = (k.power&.to_i || 1) * (u.power_numerator&.to_f || 1)
      object << { prefix: prefix,
                  unit: Unit.new(unit_name, exponent, prefix: prefix) }
    end
  end
end

.decompose_units_list(units) ⇒ Object



100
101
102
# File 'lib/unitsml/utility.rb', line 100

def decompose_units_list(units)
  gather_units(units.map { |u| decompose_unit(u) }.flatten)
end

.dim_id(dims) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/unitsml/utility.rb', line 78

def dim_id(dims)
  return nil if dims.nil? || dims.empty?

  dim_hash = dims.to_h { |h| [h[:dimension], h] }
  dims_vector = DIMS_VECTOR.map do |h|
    dim_hash.dig(h, :exponent)
  end.join(":")
  id = Unitsdb.dimensions.find_by_vector(dims_vector)&.id and return id.to_s

  "D_" + dims.map do |d|
    (U2D.dig(d[:unit], :symbol) || DIM2D.dig(d[:id], :symbol)) +
      (to_i_value(d[:exponent]) == 1 ? "" : float_to_display(d[:exponent]))
  end.join
end

.dimension(norm_text) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/unitsml/utility.rb', line 221

def dimension(norm_text)
  dim_id = unit_dimension_id(unit_instance(norm_text))
  return unless dim_id

  dim_attrs = { id: dim_id }
  dimid2dimensions(dim_id)&.compact&.each { |u| dimension1(u, dim_attrs) }
  xml = Model::Dimension.new(
    dim_attrs,
    lutaml_register: Configuration.context.id,
  ).to_xml
  xml.force_encoding("UTF-8")
end

.dimension1(dim, dims_hash) ⇒ Object



234
235
236
237
238
239
240
241
242
# File 'lib/unitsml/utility.rb', line 234

def dimension1(dim, dims_hash)
  dim_name = dim[:dimension]
  dim_klass = Model::DimensionQuantities.const_get(dim_name)
  dims_hash[underscore(dim_name).to_sym] = dim_klass.new(
    symbol: dim[:symbol],
    power_numerator: float_to_display(dim[:exponent]),
    lutaml_register: Configuration.context.id,
  )
end

.dimension_components(dims) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
# File 'lib/unitsml/utility.rb', line 359

def dimension_components(dims)
  return if dims.nil? || dims.empty?

  dim_attrs = { id: dim_id(dims) }
  dims.map { |u| dimension1(u, dim_attrs) }
  xml = Model::Dimension.new(
    **dim_attrs,
    lutaml_register: Configuration.context.id,
  ).to_xml
  xml.force_encoding("UTF-8")
end

.dimid2dimensions(normtext) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/unitsml/utility.rb', line 253

def dimid2dimensions(normtext)
  dims = Unitsdb.dimensions.find_by_id(normtext)
  dims&.processed_keys&.map do |processed_key|
    humanized = processed_key.split("_").map(&:capitalize).join
    next unless DIMS_VECTOR.include?(humanized)

    dim_quantity = dims.public_send(processed_key)
    {
      dimension: humanized,
      symbol: dim_quantity.symbol,
      exponent: dim_quantity.power,
    }
  end
end

.float_to_display(float) ⇒ Object



244
245
246
247
248
249
250
251
# File 'lib/unitsml/utility.rb', line 244

def float_to_display(float)
  case float
  when Integer, Float
    float.to_f.round(1).to_s.sub(/\.0$/, "")
  when Number, Fenced
    float.float_to_display
  end
end

.format_unit_id(unit, text) ⇒ Object



353
354
355
356
357
# File 'lib/unitsml/utility.rb', line 353

def format_unit_id(unit, text)
  return unit_nist_id(unit)&.gsub("'", "_") if unit

  text&.gsub("*", ".")&.gsub("^", "")
end

.gather_units(units) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/unitsml/utility.rb', line 124

def gather_units(units)
  units.sort_by { |a| a[:unit]&.unit_name }.each_with_object([]) do |k, m|
    if m.empty? || m[-1][:unit]&.unit_name != k[:unit]&.unit_name
      m << k
    else
      m[-1][:unit]&.power_numerator = Number.new(numerator_value(k, m))
      m[-1] = {
        prefix: combine_prefixes(m[-1][:prefix], k[:prefix]),
        unit: m[-1][:unit],
      }
    end
  end
end

.html_entity_to_unicode(string) ⇒ Object



438
439
440
# File 'lib/unitsml/utility.rb', line 438

def html_entity_to_unicode(string)
  HTMLEntities.new.decode(string)
end

.model_quantity_xml(id, url) ⇒ Object



412
413
414
415
416
417
418
419
420
# File 'lib/unitsml/utility.rb', line 412

def model_quantity_xml(id, url)
  xml = Model::Quantity.new(
    id: id,
    name: quantity_name(id),
    dimension_url: url,
    lutaml_register: Configuration.context.id,
  ).to_xml
  xml.force_encoding("UTF-8")
end

.numerator_value(unit_hash, unit_array) ⇒ Object



138
139
140
141
# File 'lib/unitsml/utility.rb', line 138

def numerator_value(unit_hash, unit_array)
  unit_numerator_float(unit_hash) +
    unit_numerator_float(unit_array[-1])
end

.prefix_attributes(prefix, options) ⇒ Object



283
284
285
286
287
288
289
290
291
292
# File 'lib/unitsml/utility.rb', line 283

def prefix_attributes(prefix, options)
  adapter = PrefixAdapter.wrap(prefix)
  {
    prefix_base: adapter.base,
    prefix_power: adapter.power,
    id: prefix_id(prefix, adapter),
    name: prefix_name(prefix, adapter),
    symbol: prefix_symbols(prefix, options),
  }
end

.prefix_id(prefix, adapter) ⇒ Object



294
295
296
297
298
299
300
301
302
# File 'lib/unitsml/utility.rb', line 294

def prefix_id(prefix, adapter)
  if prefix.is_a?(Unitsml::Prefix)
    prefix.id
  else
    adapter.raw&.identifiers&.find do |i|
      i.type == "nist"
    end&.id
  end
end

.prefix_name(prefix, adapter) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
# File 'lib/unitsml/utility.rb', line 304

def prefix_name(prefix, adapter)
  name_value = if prefix.is_a?(Unitsml::Prefix)
                 prefix.name
               elsif adapter.raw.is_a?(::Unitsdb::Prefix)
                 adapter.raw.names.find { |n| n.lang == "en" }&.value
               end
  Model::Prefixes::Name.new(
    content: name_value,
    lutaml_register: Configuration.context.id,
  )
end

.prefix_symbols(prefix, options) ⇒ Object



316
317
318
319
320
321
322
323
324
325
# File 'lib/unitsml/utility.rb', line 316

def prefix_symbols(prefix, options)
  adapter = PrefixAdapter.wrap(prefix)
  PREFIX_SYMBOL_METHODS.map do |type, method_name|
    Model::Prefixes::Symbol.new(
      type: type,
      content: adapter.raw&.public_send(method_name, options),
      lutaml_register: Configuration.context.id,
    )
  end
end

.prefix_xml(prefix, options) ⇒ Object



275
276
277
278
279
280
281
# File 'lib/unitsml/utility.rb', line 275

def prefix_xml(prefix, options)
  xml = Model::Prefix.new(
    prefix_attributes(prefix, options),
    lutaml_register: Configuration.context.id,
  ).to_xml
  Xml::Formatter.format_prefix(xml)
end

.prefixes(units, options) ⇒ Object



268
269
270
271
272
273
# File 'lib/unitsml/utility.rb', line 268

def prefixes(units, options)
  uniq_prefixes = units.filter_map(&:prefix).uniq(&:prefix_name)
  uniq_prefixes.map do |prefix|
    prefix_xml(prefix, options)
  end.join("\n")
end

.quantity(normtext, instance) ⇒ Object



371
372
373
374
375
376
377
378
379
# File 'lib/unitsml/utility.rb', line 371

def quantity(normtext, instance)
  unit = unit_instance(normtext)
  return unless unit_or_quantity(unit, instance)

  model_quantity_xml(
    instance || unit.quantity_references&.first&.id,
    "##{unit_dimension_id(unit)}",
  )
end

.quantity_dimension_id(quantity) ⇒ Object



403
404
405
# File 'lib/unitsml/utility.rb', line 403

def quantity_dimension_id(quantity)
  quantity&.dimension_reference&.id
end

.quantity_instance(id) ⇒ Object



57
58
59
# File 'lib/unitsml/utility.rb', line 57

def quantity_instance(id)
  Unitsdb.quantities.find_by_id(id)
end

.quantity_name(id) ⇒ Object



422
423
424
425
426
427
428
429
# File 'lib/unitsml/utility.rb', line 422

def quantity_name(id)
  quantity_instance(id)&.names&.filter_map do |name|
    next unless name.lang == "en"

    Model::Quantities::Name.new(content: name.value,
                                lutaml_register: Configuration.context.id)
  end
end

.rootunits(units) ⇒ Object



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/unitsml/utility.rb', line 327

def rootunits(units)
  return if units.size == 1 && !units[0].prefix

  enum_root_units = units.map do |unit|
    attributes = { unit: unit.enumerated_name }
    attributes[:prefix] = unit.prefix_name if unit.prefix
    unit.power_numerator && unit.power_numerator != "1" and
      attributes[:power_numerator] = unit.power_numerator.raw_value
    Model::Units::EnumeratedRootUnit.new(
      **attributes,
      lutaml_register: Configuration.context.id,
    )
  end
  Model::Units::RootUnits.new(
    enumerated_root_unit: enum_root_units,
    lutaml_register: Configuration.context.id,
  )
end

.set_to_fence(set) ⇒ Object



446
447
448
449
450
# File 'lib/unitsml/utility.rb', line 446

def set_to_fence(set)
  return set if set.is_a?(Fenced)

  Fenced.new("(", set, ")")
end

.string_to_html_entity(string) ⇒ Object



431
432
433
434
435
436
# File 'lib/unitsml/utility.rb', line 431

def string_to_html_entity(string)
  HTMLEntities.new.encode(
    string.frozen? ? string : string.force_encoding("UTF-8"),
    :hexadecimal,
  )
end

.to_i_value(object) ⇒ Object



93
94
95
96
97
98
# File 'lib/unitsml/utility.rb', line 93

def to_i_value(object)
  case object
  when Integer, Float then object
  when Number, Fenced then object.to_i
  end
end

.underscore(str) ⇒ Object



442
443
444
# File 'lib/unitsml/utility.rb', line 442

def underscore(str)
  str.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
end

.unit(units, formula, dims, norm_text, name, options) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/unitsml/utility.rb', line 164

def unit(units, formula, dims, norm_text, name, options)
  attributes = {
    id: unit_id(norm_text),
    system: unitsystem(units),
    name: unitname(norm_text, name),
    symbol: unitsymbols(formula, options),
    root_units: rootunits(units),
  }
  attributes[:dimension_url] = "##{dim_id(dims)}" if dims
  xml = Model::Unit.new(
    **attributes,
    lutaml_register: Configuration.context.id,
  ).to_xml
  Xml::Formatter.format_unit(xml)
end

.unit_dimension_id(unit) ⇒ Object



395
396
397
398
399
400
401
# File 'lib/unitsml/utility.rb', line 395

def unit_dimension_id(unit)
  return unless unit
  return unit.dimension_url if unit.is_a?(Unitsml::Unitsdb::Unit)

  unit.dimension_reference&.id ||
    quantity_dimension_id(quantity_instance(unit.quantity_references&.first&.id))
end

.unit_en_name(unit) ⇒ Object



388
389
390
391
392
393
# File 'lib/unitsml/utility.rb', line 388

def unit_en_name(unit)
  return unless unit
  return unit.en_name if unit.is_a?(Unitsml::Unitsdb::Unit)

  unit.names&.find { |name| name.lang == "en" }&.value
end

.unit_id(text) ⇒ Object



346
347
348
349
350
351
# File 'lib/unitsml/utility.rb', line 346

def unit_id(text)
  text = text&.gsub(/[()]/, "")
  unit = unit_instance(text)

  format_unit_id(unit, text)&.insert(0, "U_")
end

.unit_instance(unit) ⇒ Object



53
54
55
# File 'lib/unitsml/utility.rb', line 53

def unit_instance(unit)
  Unitsdb.units.find_by_symbol_id(unit)
end

.unit_nist_id(unit) ⇒ Object



381
382
383
384
385
386
# File 'lib/unitsml/utility.rb', line 381

def unit_nist_id(unit)
  return unless unit
  return unit.nist_id if unit.is_a?(Unitsml::Unitsdb::Unit)

  unit.identifiers&.find { |identifier| identifier.type == "nist" }&.id
end

.unit_numerator_float(object_hash) ⇒ Object



143
144
145
# File 'lib/unitsml/utility.rb', line 143

def unit_numerator_float(object_hash)
  object_hash[:unit]&.power_numerator&.to_f || 1
end

.unit_or_quantity(unit, quantity) ⇒ Object



407
408
409
410
# File 'lib/unitsml/utility.rb', line 407

def unit_or_quantity(unit, quantity)
  (unit && unit.quantity_references.size == 1) ||
    quantity_instance(quantity)
end

.unitname(text, name) ⇒ Object



180
181
182
183
184
185
186
# File 'lib/unitsml/utility.rb', line 180

def unitname(text, name)
  name ||= unit_en_name(unit_instance(text)) || text
  Model::Units::Name.new(
    name: name,
    lutaml_register: Configuration.context.id,
  )
end

.units2dimensions(units) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/unitsml/utility.rb', line 61

def units2dimensions(units)
  norm = decompose_units_list(units)
  return if norm.any? do |u|
    u.nil? || u[:unit]&.unknown_name? || u[:prefix] == UNKNOWN
  end

  norm.map do |u|
    unit_name = u[:unit].unit_name
    {
      dimension: U2D[unit_name][:dimension],
      unit: unit_name,
      exponent: u[:unit].power_numerator || 1,
      symbol: U2D[unit_name][:symbol],
    }
  end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
end

.unitsymbols(formula, options) ⇒ Object



188
189
190
191
192
193
194
195
196
# File 'lib/unitsml/utility.rb', line 188

def unitsymbols(formula, options)
  %w[HTML MathMl].map do |lang|
    Model::Units::Symbol.new(
      type: lang,
      content: formula.public_send(:"to_#{lang.downcase}", options),
      lutaml_register: Configuration.context.id,
    )
  end
end

.unitsystem(units) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/unitsml/utility.rb', line 198

def unitsystem(units)
  ret = []
  if units.any? { |u| !u.si_system_type? }
    ret << Model::Units::System.new(
      name: "not_SI",
      type: "not_SI",
      lutaml_register: Configuration.context.id,
    )
  end
  if units.any?(&:si_system_type?)
    if units.size == 1
      base = units[0].downcase_system_type == "si_base"
      base = true if units[0].unit_name == "g" && units[0]&.prefix_name == "k"
    end
    ret << Model::Units::System.new(
      name: "SI",
      type: (base ? "SI_base" : "SI_derived"),
      lutaml_register: Configuration.context.id,
    )
  end
  ret
end