afscgap.convert
Logic to convert types when interacting with the AFSC GAP REST service.
(c) 2024 Regents of University of California / The Eric and Wendy Schmidt Center for Data Science and the Environment at UC Berkeley.
This file is part of afscgap released under the BSD 3-Clause License. See LICENSE.md.
1""" 2Logic to convert types when interacting with the AFSC GAP REST service. 3 4(c) 2024 Regents of University of California / The Eric and Wendy Schmidt Center 5for Data Science and the Environment at UC Berkeley. 6 7This file is part of afscgap released under the BSD 3-Clause License. See 8LICENSE.md. 9""" 10import re 11 12from afscgap.typesdef import OPT_FLOAT 13 14ISO_8601_REGEX = re.compile('(?P<year>\\d{4})\\-(?P<month>\\d{2})\\-' + \ 15 '(?P<day>\\d{2})T(?P<hours>\\d{2})\\:(?P<minutes>\\d{2})\\:' + \ 16 '(?P<seconds>\\d{2})') 17 18CONVERTERS = { 19 'area': { 20 'ha': lambda x: x, 21 'm2': lambda x: x * 10000, 22 'km2': lambda x: x * 0.01 23 }, 24 'distance': { 25 'm': lambda x: x, 26 'km': lambda x: x / 1000 27 }, 28 'temperature': { 29 'c': lambda x: x, 30 'f': lambda x: x * 9 / 5 + 32 31 }, 32 'time': { 33 'day': lambda x: x / 24, 34 'hr': lambda x: x, 35 'min': lambda x: x * 60 36 }, 37 'weight': { 38 'g': lambda x: x * 1000, 39 'kg': lambda x: x 40 }, 41 'degrees': { 42 'dd': lambda x: x 43 }, 44 'effortWeight': { 45 'kg/ha': lambda x: x / 100, 46 'kg1000/km2': lambda x: x / 0.1, 47 'kg/km2': lambda x: x 48 }, 49 'effortCount': { 50 'no/ha': lambda x: x / 100, 51 'no1000/km2': lambda x: x / 0.1, 52 'no/km2': lambda x: x, 53 'count/ha': lambda x: x / 100, 54 'count1000/km2': lambda x: x / 0.1, 55 'count/km2': lambda x: x 56 } 57} 58 59UNCONVERTERS = { 60 'area': { 61 'ha': lambda x: x, 62 'm2': lambda x: x / 10000, 63 'km2': lambda x: x / 0.01 64 }, 65 'distance': { 66 'm': lambda x: x, 67 'km': lambda x: x * 1000 68 }, 69 'temperature': { 70 'c': lambda x: x, 71 'f': lambda x: (x - 32) * 5 / 9 72 }, 73 'time': { 74 'day': lambda x: x * 24, 75 'hr': lambda x: x, 76 'min': lambda x: x / 60 77 }, 78 'weight': { 79 'g': lambda x: x / 1000, 80 'kg': lambda x: x 81 }, 82 'degrees': { 83 'dd': lambda x: x 84 }, 85 'effortWeight': { 86 'kg/ha': lambda x: x * 100, 87 'kg1000/km2': lambda x: x * 0.1, 88 'kg/km2': lambda x: x 89 }, 90 'effortCount': { 91 'no/ha': lambda x: x * 100, 92 'no1000/km2': lambda x: x * 0.1, 93 'no/km2': lambda x: x, 94 'count/ha': lambda x: x * 100, 95 'count1000/km2': lambda x: x * 0.1, 96 'count/km2': lambda x: x 97 } 98} 99 100UNIT_TYPES = { 101 'ha': 'area', 102 'm2': 'area', 103 'km2': 'area', 104 'm': 'distance', 105 'km': 'distance', 106 'c': 'temperature', 107 'f': 'temperature', 108 'day': 'time', 109 'hr': 'time', 110 'min': 'time', 111 'g': 'weight', 112 'kg': 'weight', 113 'dd': 'degrees', 114 'kg/ha': 'effortWeight', 115 'kg1000/km2': 'effortWeight', 116 'kg/km2': 'effortWeight', 117 'no/ha': 'effortCount', 118 'no1000/km2': 'effortCount', 119 'no/km2': 'effortCount', 120 'count/ha': 'effortCount', 121 'count1000/km2': 'effortCount', 122 'count/km2': 'effortCount' 123} 124 125 126def is_iso8601(target: str) -> bool: 127 """Determine if a string matches an expected ISO 8601 format. 128 129 Args: 130 target: The string to test. 131 132 Returns: 133 True if it matches the expected format and false otherwise. 134 """ 135 return ISO_8601_REGEX.match(target) is not None 136 137 138def convert(target: OPT_FLOAT, source: str, destination: str) -> OPT_FLOAT: 139 """Convert a value. 140 141 Args: 142 target: The value to convert. 143 source: Original units. 144 destination: Target units. 145 146 Returns: 147 The converted value. Note that, if target is None, will return None. 148 """ 149 if target is None: 150 return None 151 152 if source not in UNIT_TYPES: 153 raise RuntimeError('Unknown units: %s' % source) 154 155 if destination not in UNIT_TYPES: 156 raise RuntimeError('Unknown units: %s' % destination) 157 158 source_type = UNIT_TYPES[source] 159 destination_type = UNIT_TYPES[destination] 160 161 if source_type != destination_type: 162 raise RuntimeError('Cannot convert from %s to %s' % (source, destination)) 163 164 source_converter = UNCONVERTERS[source_type][source] 165 destination_converter = CONVERTERS[destination_type][destination] 166 167 unconverted = source_converter(target) 168 converted = destination_converter(unconverted) 169 170 return converted
ISO_8601_REGEX =
re.compile('(?P<year>\\d{4})\\-(?P<month>\\d{2})\\-(?P<day>\\d{2})T(?P<hours>\\d{2})\\:(?P<minutes>\\d{2})\\:(?P<seconds>\\d{2})')
CONVERTERS =
{'area': {'ha': <function <lambda>>, 'm2': <function <lambda>>, 'km2': <function <lambda>>}, 'distance': {'m': <function <lambda>>, 'km': <function <lambda>>}, 'temperature': {'c': <function <lambda>>, 'f': <function <lambda>>}, 'time': {'day': <function <lambda>>, 'hr': <function <lambda>>, 'min': <function <lambda>>}, 'weight': {'g': <function <lambda>>, 'kg': <function <lambda>>}, 'degrees': {'dd': <function <lambda>>}, 'effortWeight': {'kg/ha': <function <lambda>>, 'kg1000/km2': <function <lambda>>, 'kg/km2': <function <lambda>>}, 'effortCount': {'no/ha': <function <lambda>>, 'no1000/km2': <function <lambda>>, 'no/km2': <function <lambda>>, 'count/ha': <function <lambda>>, 'count1000/km2': <function <lambda>>, 'count/km2': <function <lambda>>}}
UNCONVERTERS =
{'area': {'ha': <function <lambda>>, 'm2': <function <lambda>>, 'km2': <function <lambda>>}, 'distance': {'m': <function <lambda>>, 'km': <function <lambda>>}, 'temperature': {'c': <function <lambda>>, 'f': <function <lambda>>}, 'time': {'day': <function <lambda>>, 'hr': <function <lambda>>, 'min': <function <lambda>>}, 'weight': {'g': <function <lambda>>, 'kg': <function <lambda>>}, 'degrees': {'dd': <function <lambda>>}, 'effortWeight': {'kg/ha': <function <lambda>>, 'kg1000/km2': <function <lambda>>, 'kg/km2': <function <lambda>>}, 'effortCount': {'no/ha': <function <lambda>>, 'no1000/km2': <function <lambda>>, 'no/km2': <function <lambda>>, 'count/ha': <function <lambda>>, 'count1000/km2': <function <lambda>>, 'count/km2': <function <lambda>>}}
UNIT_TYPES =
{'ha': 'area', 'm2': 'area', 'km2': 'area', 'm': 'distance', 'km': 'distance', 'c': 'temperature', 'f': 'temperature', 'day': 'time', 'hr': 'time', 'min': 'time', 'g': 'weight', 'kg': 'weight', 'dd': 'degrees', 'kg/ha': 'effortWeight', 'kg1000/km2': 'effortWeight', 'kg/km2': 'effortWeight', 'no/ha': 'effortCount', 'no1000/km2': 'effortCount', 'no/km2': 'effortCount', 'count/ha': 'effortCount', 'count1000/km2': 'effortCount', 'count/km2': 'effortCount'}
def
is_iso8601(target: str) -> bool:
127def is_iso8601(target: str) -> bool: 128 """Determine if a string matches an expected ISO 8601 format. 129 130 Args: 131 target: The string to test. 132 133 Returns: 134 True if it matches the expected format and false otherwise. 135 """ 136 return ISO_8601_REGEX.match(target) is not None
Determine if a string matches an expected ISO 8601 format.
Arguments:
- target: The string to test.
Returns:
True if it matches the expected format and false otherwise.
def
convert( target: Optional[float], source: str, destination: str) -> Optional[float]:
139def convert(target: OPT_FLOAT, source: str, destination: str) -> OPT_FLOAT: 140 """Convert a value. 141 142 Args: 143 target: The value to convert. 144 source: Original units. 145 destination: Target units. 146 147 Returns: 148 The converted value. Note that, if target is None, will return None. 149 """ 150 if target is None: 151 return None 152 153 if source not in UNIT_TYPES: 154 raise RuntimeError('Unknown units: %s' % source) 155 156 if destination not in UNIT_TYPES: 157 raise RuntimeError('Unknown units: %s' % destination) 158 159 source_type = UNIT_TYPES[source] 160 destination_type = UNIT_TYPES[destination] 161 162 if source_type != destination_type: 163 raise RuntimeError('Cannot convert from %s to %s' % (source, destination)) 164 165 source_converter = UNCONVERTERS[source_type][source] 166 destination_converter = CONVERTERS[destination_type][destination] 167 168 unconverted = source_converter(target) 169 converted = destination_converter(unconverted) 170 171 return converted
Convert a value.
Arguments:
- target: The value to convert.
- source: Original units.
- destination: Target units.
Returns:
The converted value. Note that, if target is None, will return None.