File: proj.py

package info (click to toggle)
thuban 1.2.2-14
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 9,176 kB
  • sloc: python: 30,410; ansic: 6,181; xml: 4,234; cpp: 1,595; makefile: 145
file content (216 lines) | stat: -rw-r--r-- 6,654 bytes parent folder | download | duplicates (5)
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
# Copyright (c) 2001, 2003, 2006 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
# Bernhard Reiter <bernhard@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.
__version__ = "$Revision: 2756 $"

from types import StringTypes
import locale

from Thuban import _
from Thuban.Lib.connector import Publisher

import Projection
BaseProjection = Projection.Projection
del Projection

from messages import PROJECTION_ADDED, PROJECTION_REPLACED, PROJECTION_REMOVED

PROJ_UNITS_METERS  = 1
PROJ_UNITS_DEGREES = 2

def _do_we_have_to_work_around_broken_proj():
    """If we have a problematic locale, check if proj results are good."""
    if locale.localeconv()['decimal_point'] != '.':
        params = ["proj=latlong", "to_meter=0.01745", "ellps=clrk66"]
        proj = BaseProjection(params)
        result1 = proj.Forward(1,1)

        # using setlocale because of python 2.[345] problem with getlocale here
        savedlocale = locale.setlocale(locale.LC_NUMERIC)
        locale.setlocale(locale.LC_NUMERIC, "C")

        proj = BaseProjection(params)
        result2 = proj.Forward(1,1)

        locale.setlocale(locale.LC_NUMERIC, savedlocale)

        if result1 != result2:
            return True

    return False

class Projection(BaseProjection):
    """A proj4 projection object that remembers the parameters.

    The proj library is not robust against decimal_point != '.' locales.
    Since python 2.4 calls C extensions with the set locale, it can create
    a problem.  It seems that calling
        self.assuregoodlocale()
        self.assureinitlocale()
    before BaseProjection.__init__() is enough to work around this.

    We assuming that the locale stays the same after a projection
    has been initialised 
    and thus we can return to it in self.assureinitlocale().
    """

    def __init__(self, params, name = None, epsg = None):
        """Initialize the Projection

        Parameters:

        params -- a list of 'parameter=value' strings

        name -- (optional) The name of the projection. If None or omitted
                it defaults to 'Unknown' in the local language.

        epsg -- (optional) The EPSG code as a string.
        """
        # using setlocale because of python 2.[345] problem with getlocale here
        self.initlocale = locale.setlocale(locale.LC_NUMERIC)
        self.work_around_broken_proj = _do_we_have_to_work_around_broken_proj()

        self.assuregoodlocale()
        BaseProjection.__init__(self, params)
        self.assureinitlocale()

        if name is None:
            self.name = _("Unknown")
        elif isinstance(name, StringTypes):
            self.name = name

        self.epsg = epsg
        self.params = params

    def assuregoodlocale(self):
        if self.work_around_broken_proj:
            locale.setlocale(locale.LC_NUMERIC, "C")

    def assureinitlocale(self):
        if self.work_around_broken_proj:
            locale.setlocale(locale.LC_NUMERIC, self.initlocale)

    def _transform_bbox(self, trafo, bbox):
        # This is not really the correct way to determine the bbox of a
        # projected bbox, but for now it works well enough
        llx, lly, urx, ury = bbox
        xs = []; ys = []
        for x, y in ((llx, lly), (llx, ury), (urx, lly), (urx, ury)):
            x, y = trafo(x, y)
            xs.append(x); ys.append(y)
        return min(xs), min(ys), max(xs), max(ys)

    def ForwardBBox(self, bbox):
        """Return the bounding box of the corners of the bounding box bbox
        """
        return self._transform_bbox(self.Forward, bbox)

    def InverseBBox(self, bbox):
        return self._transform_bbox(self.Inverse, bbox)

    def GetName(self):
        """Return the name of the projection."""
        return self.name

    def Label(self):
        if self.epsg:
            return "EPSG % 5s %s" % (self.epsg, self.name)
        return self.name

    def EPSGCode(self):
        """Return the EPSG code as a string or None if there is none"""
        return self.epsg

    def GetParameter(self, param):
        """Return the projection value for the given parameter.

        If 'param' exists as a valid parameter return the associated
        value as a string. If the parameter does not have a value (like
        e.g. the 'south' parameter for utm) then the value is the
        parameter name itself.

        If the parameter doesn't exist return an empty string.
        """

        for pair in self.params:
            if "=" in pair:
                p, v = pair.split("=")
            else:
                p = v = pair
            if p == param:
                return v

        return ""

    def GetAllParameters(self):
        """Return list of 'parameter=value' strings"""
        return self.params

    def GetProjectedUnits(self):
        if self.GetParameter("proj") in [ 'latlong', 'longlat' ]:
            return PROJ_UNITS_DEGREES
        else:
            return PROJ_UNITS_METERS

    def __repr__(self):
        return self.name + ": " + repr(self.params)


class ProjFile(Publisher):

    def __init__(self, filename):
        """Intialize the ProjFile.

        filename -- name of the file that this ProjFile represents.
        """

        self.__projs = []

        self.SetFilename(filename)

    def Add(self, proj):
        """Add the projection to the end of the file."""
        self.__projs.append(proj)
        self.issue(PROJECTION_ADDED, proj)

    def Remove(self, proj):
        """Remove the object proj from the projection file.

        Raises a ValueError is proj is not found.
        """
        self.__projs.remove(proj)
        self.issue(PROJECTION_REMOVED, proj)

    def Replace(self, oldproj, newproj):
        """Replace the object 'oldproj' with 'newproj'.

        Raises ValueError if oldproj is not in the file.
        """
        self.__projs[self.__projs.index(oldproj)] = newproj
        self.issue(PROJECTION_REPLACED, oldproj, newproj)

    def GetFilename(self):
        """Return the filename where the ProjFile was read or will be
        written to.
        """

        return self.__filename

    def SetFilename(self, filename):
        """Set the filename where the ProjFile will be written to."""
        self.__filename = filename

    def GetProjections(self):
        """Return a list of the projections in the order they were read
        from the file or will be written.

        This is not a deep copy list, so any modifications made to the
        Projection objects will be written to the file.
        """

        return self.__projs