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

Class Method Summary collapse

Class Method Details

.combine_prefixes(p1, p2) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/unitsml/utility.rb', line 130

def combine_prefixes(p1, p2)
  return nil if p1.nil? && p2.nil?
  return p1.symbolid if p2.nil?
  return p2.symbolid if p1.nil?
  return "unknown" if p1.base != p2.base

  Unitsdb.prefixes_array.each do |prefix_name|
    p = prefix_object(prefix_name)
    return p if p.base == p1.base && p.power == p1.power + p2.power
  end

  "unknown"
end

.decompose_unit(u) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/unitsml/utility.rb', line 88

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?
                 combine_prefixes(prefix_object(k.prefix_reference), u.prefix)
               else
                 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



84
85
86
# File 'lib/unitsml/utility.rb', line 84

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

.dim_id(dims) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/unitsml/utility.rb', line 71

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

  dim_hash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
  dims_vector = DIMS_VECTOR.map { |h| dim_hash.dig(h, :exponent) }.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)) +
      (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
  end.join("")
end

.dimension(norm_text) ⇒ Object



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

def dimension(norm_text)
  dim_id = unit_instance(norm_text)&.dimension_url
  return unless dim_id

  dim_attrs = { id: dim_id }
  dimid2dimensions(dim_id)&.compact&.each { |u| dimension1(u, dim_attrs) }
  Model::Dimension.new(dim_attrs).to_xml
end

.dimension1(dim, dims_hash) ⇒ Object



199
200
201
202
203
204
205
206
# File 'lib/unitsml/utility.rb', line 199

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])
  )
end

.dimension_components(dims) ⇒ Object



263
264
265
266
267
268
269
# File 'lib/unitsml/utility.rb', line 263

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

  dim_attrs = { id: dim_id(dims) }
  dims.map { |u| dimension1(u, dim_attrs) }
  Model::Dimension.new(dim_attrs).to_xml
end

.dimid2dimensions(normtext) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/unitsml/utility.rb', line 212

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



208
209
210
# File 'lib/unitsml/utility.rb', line 208

def float_to_display(float)
  float.to_f.round(1).to_s.sub(/\.0$/, "")
end

.gather_units(units) ⇒ Object



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

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 = (k[:unit]&.power_numerator&.to_f || 1) + (m[-1][:unit]&.power_numerator&.to_f || 1)
      m[-1] = {
        prefix: combine_prefixes(prefix_object(m[-1][:prefix]), prefix_object(k[:prefix])),
        unit: m[-1][:unit],
      }
    end
  end
end

.html_entity_to_unicode(string) ⇒ Object



299
300
301
# File 'lib/unitsml/utility.rb', line 299

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

.prefix_object(prefix) ⇒ Object



123
124
125
126
127
128
# File 'lib/unitsml/utility.rb', line 123

def prefix_object(prefix)
  return prefix unless prefix.is_a?(String)
  return nil unless Unitsdb.prefixes_array.any?(prefix)

  Prefix.new(prefix)
end

.prefixes(units, options) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/unitsml/utility.rb', line 227

def prefixes(units, options)
  uniq_prefixes = units.map { |unit| unit.prefix }.compact.uniq { |d| d.prefix_name }
  uniq_prefixes.map do |prefix|
    prefix_attrs = { prefix_base: prefix&.base, prefix_power: prefix&.power, id: prefix&.id }
    type_and_methods = { ASCII: :to_asciimath, unicode: :to_unicode, LaTeX: :to_latex, HTML: :to_html }
    prefix_attrs[:name] = Model::Prefixes::Name.new(content: prefix&.name)
    prefix_attrs[:symbol] = type_and_methods.map do |type, method_name|
      Model::Prefixes::Symbol.new(
        type: type,
        content: prefix&.public_send(method_name, options),
      )
    end
    Model::Prefix.new(prefix_attrs).to_xml.gsub("&amp;", "&")
  end.join("\n")
end

.quantity(normtext, quantity) ⇒ Object



271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/unitsml/utility.rb', line 271

def quantity(normtext, quantity)
  unit = unit_instance(normtext)
  return unless unit && unit.quantity_references.size == 1 ||
    quantity_instance(quantity)

  id = (quantity || unit.quantity_references&.first&.id)
  Model::Quantity.new(
    id: id,
    name: quantity_name(id),
    dimension_url: "##{unit.dimension_url}",
  ).to_xml
end

.quantity_instance(id) ⇒ Object



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

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

.quantity_name(id) ⇒ Object



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

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

    Model::Quantities::Name.new(content: name.value)
  end
end

.rootunits(units) ⇒ Object



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

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
    Model::Units::EnumeratedRootUnit.new(attributes)
  end
  Model::Units::RootUnits.new(enumerated_root_unit: enum_root_units)
end

.string_to_html_entity(string) ⇒ Object



292
293
294
295
296
297
# File 'lib/unitsml/utility.rb', line 292

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

.underscore(str) ⇒ Object



303
304
305
# File 'lib/unitsml/utility.rb', line 303

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

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



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

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
  Model::Unit.new(attributes).to_xml
    .gsub("&lt;", "<")
    .gsub("&gt;", ">")
    .gsub("&amp;", "&")
    .gsub(//, "&#x2212;")
    .gsub(//, "&#x22c5;")
end

.unit_id(text) ⇒ Object



256
257
258
259
260
261
# File 'lib/unitsml/utility.rb', line 256

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

  "U_#{unit ? unit.nist_id&.gsub(/'/, '_') : text&.gsub(/\*/, '.')&.gsub(/\^/, '')}"
end

.unit_instance(unit) ⇒ Object



48
49
50
# File 'lib/unitsml/utility.rb', line 48

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

.unitname(text, name) ⇒ Object



161
162
163
164
# File 'lib/unitsml/utility.rb', line 161

def unitname(text, name)
  name ||= unit_instance(text)&.en_name || text
  Model::Units::Name.new(name: name)
end

.units2dimensions(units) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/unitsml/utility.rb', line 56

def units2dimensions(units)
  norm = decompose_units_list(units)
  return if norm.any? { |u| u.nil? || u[:unit].unit_name == "unknown" || u[:prefix] == "unknown" }

  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



166
167
168
169
170
171
172
173
# File 'lib/unitsml/utility.rb', line 166

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

.unitsystem(units) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/unitsml/utility.rb', line 175

def unitsystem(units)
  ret = []
  if units.any? { |u| !u.si_system_type? }
    ret << Model::Units::System.new(name: "not_SI", type: "not_SI")
  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'))
  end
  ret
end