2"""Unit conversion on the basis of `SPARQL/WIKIDATA Precision, Units and
5.. _SPARQL/WIKIDATA Precision, Units and Coordinates:
6 https://en.wikibooks.org/wiki/SPARQL/WIKIDATA_Precision,_Units_and_Coordinates#Quantities
8from __future__
import annotations
10__all__ = [
"convert_from_si",
"convert_to_si",
"symbol_to_si"]
19 """The mapping of the Beaufort_ contains values from 0 to 16 (55.6 m/s),
20 wind speeds greater than 200km/h (55.6 m/s) are given as 17 Bft. Thats why
21 a value of 17 Bft cannot be converted to SI.
25 Negative values or values greater 16 Bft (55.6 m/s) will throw a
28 _Beaufort: https://en.wikipedia.org/wiki/Beaufort_scale
32 scale: list[float] = [
33 0.2, 1.5, 3.3, 5.4, 7.9,
34 10.7, 13.8, 17.1, 20.7, 24.4,
35 28.4, 32.6, 32.7, 41.1, 45.8,
42 if value < 0
or value > 55.6:
43 raise ValueError(f
"invalid value {value} / the Beaufort scales from 0 to 16 (55.6 m/s)")
45 for bft, mps
in enumerate(cls.
scale):
53 if idx < 0
or idx > 16:
54 raise ValueError(f
"invalid value {value} / the Beaufort scales from 0 to 16 (55.6 m/s)")
62 "to_si":
lambda val: val + 273.15,
63 "from_si":
lambda val: val - 273.15,
68 "to_si":
lambda val: (val + 459.67) * 5 / 9,
69 "from_si":
lambda val: (val * 9 / 5) - 459.67,
74 "to_si": Beaufort.to_si,
75 "from_si": Beaufort.from_si,
78"""Additional items to convert from a measure unit to a SI unit (vice versa).
83 "si_name": "Q11579", # Wikidata item ID of the SI unit (Kelvin)
84 "symbol": "°C", # symbol of the measure unit
85 "to_si": lambda val: val + 273.15, # convert measure value (val) to SI unit
86 "from_si": lambda val: val - 273.15, # convert SI value (val) measure unit
91 "to_si": 1609.344, # convert measure value (val) to SI unit
92 "from_si": 1 / 1609.344 # convert SI value (val) measure unit
95The values of ``to_si`` and ``from_si`` can be of :py:obj:`float` (a multiplier)
96or a callable_ (val in / converted value returned).
98.. _callable: https://docs.python.org/3/glossary.html#term-callable
108"""Alias symbols for known unit of measure symbols / by example::
110 '°C': ('C', ...), # list of alias symbols for °C (Q69362731)
111 '°F': ('F', ...), # list of alias symbols for °F (Q99490479)
112 'mi': ('L',), # list of alias symbols for mi (Q253276)
117UNITS_BY_SI_NAME: dict = {}
122 if isinstance(from_si, (float, int)):
123 value = float(value) * from_si
125 value = from_si(float(value))
131 if isinstance(to_si, (float, int)):
132 value = float(value) * to_si
134 value = to_si(float(value))
140 global UNITS_BY_SI_NAME
142 return UNITS_BY_SI_NAME[si_name]
147 item_si_name = item[pos_si_name]
148 item_symbol = item[pos_symbol]
150 by_symbol = UNITS_BY_SI_NAME.get(item_si_name)
151 if by_symbol
is None:
153 UNITS_BY_SI_NAME[item_si_name] = by_symbol
154 by_symbol[item_symbol] = item
156 return UNITS_BY_SI_NAME[si_name]
167 """Generates a list of tuples, each tuple is a measure unit and the fields
170 0. Symbol of the measure unit (e.g. 'mi' for measure unit 'miles' Q253276)
172 1. SI name of the measure unit (e.g. Q11573 for SI unit 'metre')
174 2. Factor to get SI value from measure unit (e.g. 1mi is equal to SI 1m
175 multiplied by 1609.344)
177 3. Factor to get measure value from from SI value (e.g. SI 100m is equal to
178 100mi divided by 1609.344)
180 The returned list is sorted, the first items are created from
181 ``WIKIDATA_UNITS``, the second group of items is build from
182 :py:obj:`ADDITIONAL_UNITS` and items created from :py:obj:`ALIAS_SYMBOLS`.
184 If you search this list for a symbol, then a match with a symbol from
185 Wikidata has the highest weighting (first hit in the list), followed by the
186 symbols from the :py:obj:`ADDITIONAL_UNITS` and the lowest weighting is
187 given to the symbols resulting from the aliases :py:obj:`ALIAS_SYMBOLS`.
199 for item
in data.WIKIDATA_UNITS.values():
200 if item[
'to_si_factor']
and item[
'symbol']:
205 1 / item[
'to_si_factor'],
206 item[
'to_si_factor'],
211 for item
in ADDITIONAL_UNITS:
223 for item
in SYMBOL_TO_SI:
224 for alias
in ALIAS_SYMBOLS.get(item[0], ()):
234 SYMBOL_TO_SI = SYMBOL_TO_SI + alias_items
251SELECT DISTINCT ?item ?symbol ?tosi ?tosiUnit
254 ?item wdt:P31/wdt:P279 wd:Q47574 .
255 ?item p:P5061 ?symbolP .
256 ?symbolP ps:P5061 ?symbol ;
257 wikibase:rank ?rank .
259 ?item p:P2370 ?tosistmt .
260 ?tosistmt psv:P2370 ?tosinode .
261 ?tosinode wikibase:quantityAmount ?tosi .
262 ?tosinode wikibase:quantityUnit ?tosiUnit .
264 FILTER(LANG(?symbol) = "en").
266ORDER BY ?item DESC(?rank) ?symbol
271 """Fetch units from Wikidata. Function is used to update persistence of
272 :py:obj:`searx.data.WIKIDATA_UNITS`."""
274 results = collections.OrderedDict()
275 response = wikidata.send_wikidata_query(SARQL_REQUEST)
276 for unit
in response[
'results'][
'bindings']:
278 symbol = unit[
'symbol'][
'value']
279 name = unit[
'item'][
'value'].rsplit(
'/', 1)[1]
280 si_name = unit.get(
'tosiUnit', {}).get(
'value',
'')
282 si_name = si_name.rsplit(
'/', 1)[1]
284 to_si_factor = unit.get(
'tosi', {}).get(
'value',
'')
285 if name
not in results:
289 'si_name': si_name
if si_name
else None,
290 'to_si_factor': float(to_si_factor)
if to_si_factor
else None,
float from_si(cls, value)
float convert_from_si(str si_name, str symbol, float|int value)
float convert_to_si(str si_name, str symbol, float|int value)
units_by_si_name(si_name)