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

.class_name(klass) ⇒ Object



311
312
313
# File 'lib/unitsml/utility.rb', line 311

def class_name(klass)
  klass.name.split("::").last
end

.combine_prefixes(p1, p2) ⇒ Object



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

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



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

def decompose_unit(u)
  if u&.unit_name == "g" || 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.nil? && !k.prefix.empty?
                 combine_prefixes(prefix_object(k.prefix), u.prefix)
               else
                 u.prefix
               end
      unit_name = Unitsdb.units.find_by_id(k.id).unit_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



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

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



195
196
197
198
199
200
201
202
203
# File 'lib/unitsml/utility.rb', line 195

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

  dim_id = dim_url.sub("#", '')
  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



205
206
207
208
209
210
211
212
# File 'lib/unitsml/utility.rb', line 205

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



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

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



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

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_numerator,
    }
  end
end

.display_exp(unit) ⇒ Object



170
171
172
# File 'lib/unitsml/utility.rb', line 170

def display_exp(unit)
  unit.power_numerator && unit.power_numerator != "1" ? "^#{unit.power_numerator}" : ""
end

.float_to_display(float) ⇒ Object



214
215
216
# File 'lib/unitsml/utility.rb', line 214

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

.gather_units(units) ⇒ Object



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

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



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

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

.postprocess_normtext(units) ⇒ Object



166
167
168
# File 'lib/unitsml/utility.rb', line 166

def postprocess_normtext(units)
  units.map { |u| "#{u.prefix_name}#{u.unit_name}#{display_exp(u)}" }.join("*")
end

.prefix_object(prefix) ⇒ Object



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

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



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/unitsml/utility.rb', line 233

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



277
278
279
280
281
282
283
284
285
286
# File 'lib/unitsml/utility.rb', line 277

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

  id = (quantity || unit.quantity_reference&.first&.url).sub('#', '')
  dim_url = unit.dimension_url
  attributes = { id: id, name: quantity_name(id), dimension_url: dim_url }
  Model::Quantity.new(attributes).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



288
289
290
291
292
# File 'lib/unitsml/utility.rb', line 288

def quantity_name(id)
  quantity_instance(id)&.quantity_name&.map do |content|
    Model::Quantities::Name.new(content: content)
  end
end

.rootunits(units) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/unitsml/utility.rb', line 249

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



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

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

.underscore(str) ⇒ Object



307
308
309
# File 'lib/unitsml/utility.rb', line 307

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

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



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

def unit(units, formula, dims, norm_text, name, options)
  attributes = {
    id: unit_id(norm_text),
    system: unitsystem(units),
    name: unitname(units, 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



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

def unit_id(text)
  norm_text = text
  text = text&.gsub(/[()]/, "")
  unit = unit_instance(norm_text)
  "U_#{unit ? unit.id&.gsub(/'/, '_') : norm_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(units, text, name) ⇒ Object



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

def unitname(units, text, name)
  Model::Units::Name.new(
    name: unit_instance(text)&.unit_name&.first || text
  )
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



174
175
176
177
178
# File 'lib/unitsml/utility.rb', line 174

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



180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/unitsml/utility.rb', line 180

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