File: units.py

package info (click to toggle)
grass 8.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 277,040 kB
  • sloc: ansic: 460,798; python: 227,732; cpp: 42,026; sh: 11,262; makefile: 7,007; xml: 3,637; sql: 968; lex: 520; javascript: 484; yacc: 450; asm: 387; perl: 157; sed: 25; objc: 6; ruby: 4
file content (219 lines) | stat: -rw-r--r-- 5,766 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
"""
@package core.units

@brief Units management

.. todo::
    Probably will be replaced by Python ctypes fns in the near future(?)

Usage:

    from core.units import Units

Classes:
 - units::BaseUnits

(C) 2009, 2011 by the GRASS Development Team

This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Martin Landa <landa.martin gmail.com>
"""

import math

if __name__ == "__main__":
    import sys


class BaseUnits:
    def __init__(self):
        self._units = dict()
        self._units["length"] = {
            0: {"key": "mu", "label": _("map units")},
            1: {"key": "me", "label": _("meters")},
            2: {"key": "km", "label": _("kilometers")},
            3: {"key": "mi", "label": _("miles")},
            4: {"key": "ft", "label": _("feet")},
        }

        self._units["area"] = {
            0: {"key": "mu", "label": _("sq map units")},
            1: {"key": "me", "label": _("sq meters")},
            2: {"key": "km", "label": _("sq kilometers")},
            3: {"key": "ar", "label": _("acres")},
            4: {"key": "ht", "label": _("hectares")},
        }

    def GetUnitsList(self, type):
        """Get list of units (their labels)

        :param type: units type ('length' or 'area')

        :return: list of units labels
        """
        result = list()
        try:
            keys = sorted(self._units[type].keys())
            for idx in keys:
                result.append(self._units[type][idx]["label"])
        except KeyError:
            pass

        return result

    def GetUnitsKey(self, type, index):
        """Get units key based on index

        :param type: units type ('length' or 'area')
        :param index: units index
        """
        return self._units[type][index]["key"]

    def GetUnitsIndex(self, type, key):
        """Get units index based on key

        :param type: units type ('length' or 'area')
        :param key: units key, e.g. 'me' for meters

        :return: index
        """
        for k, u in self._units[type].items():
            if u["key"] == key:
                return k
        return 0


Units = BaseUnits()


def ConvertValue(value, type, units):
    """Convert value from map units to given units

    Inspired by vector/v.to.db/units.c

    :param value: value to be converted
    :param type: units type ('length', 'area')
    :param unit: destination units
    """
    # get map units
    # TODO

    f = 1
    if type == "length":
        if units == "me":
            f = 1.0
        elif units == "km":
            f = 1.0e-3
        elif units == "mi":
            f = 6.21371192237334e-4
        elif units == "ft":
            f = 3.28083989501312
    else:  # -> area
        if units == "me":
            f = 1.0
        elif units == "km":
            f = 1.0e-6
        elif units == "mi":
            f = 3.86102158542446e-7
        elif units == "ft":
            f = 10.7639104167097
        elif units == "ar":
            f = 2.47105381467165e-4
        elif units == "ht":
            f = 1.0e-4

    return f * value


def formatDist(distance, mapunits):
    """Formats length numbers and units in a nice way.

    Formats length numbers and units as a function of length.

    >>> formatDist(20.56915, 'metres')
    (20.57, 'm')
    >>> formatDist(6983.4591, 'metres')
    (6.983, 'km')
    >>> formatDist(0.59, 'feet')
    (0.59, 'ft')
    >>> formatDist(8562, 'feet')
    (1.622, 'miles')
    >>> formatDist(0.48963, 'degrees')
    (29.38, 'min')
    >>> formatDist(20.2546, 'degrees')
    (20.25, 'deg')
    >>> formatDist(82.146, 'unknown')
    (82.15, 'units')

    Accepted map units are 'meters', 'metres', 'feet', 'degree'.
    Returns 'units' instead of unrecognized units.

    :param distance: map units
    :param mapunits: map units

    From code by Hamish Bowman Grass Development Team 2006.
    """
    if mapunits == "metres":
        mapunits = "meters"
    outunits = mapunits
    distance = float(distance)
    divisor = 1.0

    # figure out which units to use
    if mapunits == "meters":
        if distance > 2500.0:
            outunits = "km"
            divisor = 1000.0
        else:
            outunits = "m"
    elif mapunits == "feet":
        # nano-bug: we match any "feet", but US Survey feet is really
        #  5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
        #  miles the tick markers are rounded to the nearest 10th of a
        #  mile (528'), the difference in foot flavours is ignored.
        if distance > 5280.0:
            outunits = "miles"
            divisor = 5280.0
        else:
            outunits = "ft"
    elif "degree" in mapunits:
        # was: 'degree' in mapunits and not haveCtypes (for unknown reason)
        if distance < 1:
            outunits = "min"
            divisor = 1 / 60.0
        else:
            outunits = "deg"
    else:
        return (distance, "units")

    # format numbers in a nice way
    if (distance / divisor) >= 2500.0:
        outdistance = round(distance / divisor)
    elif (distance / divisor) >= 1000.0:
        outdistance = round(distance / divisor, 1)
    elif (distance / divisor) > 0.0:
        outdistance = round(
            distance / divisor, int(math.ceil(3 - math.log10(distance / divisor)))
        )
    else:
        outdistance = float(distance / divisor)

    return (outdistance, outunits)


def doc_test():
    """Tests the module using doctest

    :return: a number of failed tests
    """
    import doctest
    from core.utils import do_doctest_gettext_workaround

    do_doctest_gettext_workaround()
    return doctest.testmod().failed


if __name__ == "__main__":
    sys.exit(doc_test())