Issues (58)

patty/srs.py (1 issue)

1 1
from __future__ import print_function
2 1
import numpy as np
3 1
import osgeo.osr as osr
4
5
6 1
def is_registered(pointcloud):
7
    """
8
    Returns True when a pointcloud is registered; ie coordinates are relative
9
    to a specific spatial reference system or offset.
10
11
    In that case, first transform one pointcloud to the reference system
12
    of the other, before doing processing on the points:
13
14
       set_srs(pcA, same_as=pcB)
15
    """
16 1
    return hasattr(pointcloud, 'srs') or hasattr(pointcloud, 'offset')
17
18
19 1
def same_srs(pc_one, pc_two):
20
    """
21
    True if the two pointclouds have the same coordinate system
22
23
    Arguments:
24
        pc_one : pcl.PointCloud
25
        pc_two : pc..PointCloud
26
    """
27
28
    is_reg_one = is_registered(pc_one)
29
    is_reg_two = is_registered(pc_two)
30
31
    # both pointcloud are pure pcl: no offset nor SRS
32
    if not is_reg_one and not is_reg_two:
33
        return True
34
35
    # only one of the pointclouds is registered
36
    if ((not is_reg_one) and is_reg_two) or ((not is_reg_two) and is_reg_one):
37
        return False
38
39
    srs_one = None
40
    srs_two = None
41
    try:
42
        srs_one = pc_one.srs
43
        srs_two = pc_two.srs
44
45
        if not srs_one.IsSame(srs_two):
46
            # SRS present, but different
47
            return False
48
    except:
49
        # one of the pointclouds does not have a SRS
50
        return False
51
52
    off_one = None
53
    off_two = None
54
    try:
55
        off_one = pc_one.offset
56
        off_two = pc_two.offset
57
    except:
58
        # one of the pointclouds does not have an offset
59
        return False
60
61
    # absolute(off_one - off_two) <= (atol + rtol * absolute(off_two))
62
    if not np.allclose(off_one, off_two, rtol=1e-06, atol=1e-08):
63
        return False
64
65
    return True
66
67
68 1
def set_srs(pc, srs=None, offset=np.array([0, 0, 0], dtype=np.float64),
69
            same_as=None):
70
    """Set the spatial reference system (SRS) and offset for a pointcloud.
71
    This function transforms all the points to the new reference system, and
72
    updates the metadata accordingly.
73
74
    Either give a SRS and offset, or a reference pointcloud
75
76
    NOTE: Pointclouds in PCL do not have absolute coordinates, ie.
77
          latitude / longitude. This function sets metadata to the pointcloud
78
          describing an absolute frame of reference.
79
          It is left to the user to make sure pointclouds are in the same
80
          reference system, before passing them on to PCL functions. This
81
          can be checked with patty.utils.same_srs().
82
83
    NOTE: To add a SRS to a point cloud, or to update incorrect metadata,
84
          use force_srs().
85
86
    Example:
87
88
        # set the SRS to lat/lon,
89
        # don't use an offset, so it defaults to [0,0,0]
90
        set_srs( pc, srs="EPSG:4326" )
91
92
    Arguments:
93
        pc : pcl.Pointcloud, with pcl.is_registered() == True
94
95
        same_as : pcl.PointCloud
96
97
        offset : np.array([3], dtype=np.float64 )
98
            Must be added to the points to get absolute coordinates,
99
            neccesary to retain precision for LAS pointclouds.
100
101
        srs : object or osgeo.osr.SpatialReference
102
            If it is an SpatialReference, it will be used directly.
103
            Otherwise it is passed to osr.SpatialReference.SetFromUserInput()
104
105
    Returns:
106
        pc : pcl.PointCloud
107
            The input pointcloud.
108
    """
109 1
    if not is_registered(pc):
110
        raise TypeError("Pointcloud is not registered")
111
        return None
112
113 1
    update_offset = False
114 1
    update_srs = False
115
116 1
    if same_as:
117
118
        # take offset and SRS from reference pointcloud
119 1
        update_offset = True
120 1
        update_srs = True
121 1
        if is_registered(same_as):
122 1
            newsrs = same_as.srs
123 1
            newoffset = same_as.offset
124
        else:
125
            raise TypeError("Reference pointcloud is not registered")
126
    else:
127
128
        # take offset and SRS from arguments
129
130
        # sanitize offset
131 1
        if offset is not None:
132 1
            update_offset = True
133
134 1
            newoffset = np.array(offset, dtype=np.float64)
135 1
            if len(newoffset) != 3:
136
                raise TypeError("Offset should be an np.array([3])")
137
138
        # sanitize SRS
139 1
        if srs is not None:
140
141
            # argument is a SRS, use it
142 1
            if type(srs) == type(osr.SpatialReference()):
143 1
                update_srs = True
144 1
                newsrs = srs
145
146
            # argument is not an SRS, try to convert it to one
147
            elif isinstance(srs, osr.SpatialReference):
148
                newsrs = osr.SpatialReference()
149
                if newsrs.SetFromUserInput(srs) == 0:
150
                    update_srs = True
151
152
            # illegal input
153
            else:
154
                raise TypeError(
155
                    "SRS should be a string or a osr.SpatialReference")
156
    # Apply
157
158
    # add old offset
159 1
    data = np.asarray(pc)
160 1
    precise_points = np.array(data, dtype=np.float64) + pc.offset
161
162
    # do transformation, this resets the offset to 0
163 1
    if update_srs and not pc.srs.IsSame(newsrs):
164 1
        try:
165 1
            transform = osr.CoordinateTransformation(pc.srs, newsrs)
166 1
            precise_points = np.array(transform.TransformPoints(precise_points),
0 ignored issues
show
This line is too long as per the coding-style (80/79).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
167
                                      dtype=np.float64)
168 1
            pc.srs = newsrs.Clone()
169 1
            pc.offset = np.array([0, 0, 0], dtype=np.float64)
170
        except:
171
            print("WARNING, CAN'T DO COORDINATE TRANSFORMATION")
172
173
    # substract new offset
174 1
    if update_offset:
175 1
        precise_points -= newoffset
176 1
        pc.offset = np.array(newoffset, dtype=np.float64)
177
178
    # copy the float64 to the pointcloud
179 1
    data[...] = np.asarray(precise_points, dtype=np.float32)
180
181 1
    return pc
182
183
184 1
def force_srs(pc, srs=None, offset=None, same_as=None):
185
    """
186
    Set a spatial reference system (SRS) and offset for a pointcloud.
187
    Either give a SRS and offset, or a reference pointcloud
188
    This function affects the metadata only.
189
190
    This is the recommended way to turn a python-pcl pointcloud to a
191
    registerd pointcloud with absolute coordiantes.
192
193
    NOTE: To change the SRS for an already registered pointcloud, use set_srs()
194
195
    Example:
196
197
        # set the SRS to lat/lon, leave offset unchanged
198
        force_srs( pc, srs="EPSG:4326" )
199
200
    Arguments:
201
        pc : pcl.Pointcloud
202
203
        same_as : pcl.PointCloud
204
205
        offset : np.array([3])
206
            Must be added to the points to get absolute coordinates,
207
            neccesary to retain precision for LAS pointclouds.
208
209
        srs : object or osgeo.osr.SpatialReference
210
            If it is an SpatialReference, it will be used directly.
211
            Otherwise it is passed to osr.SpatialReference.SetFromUserInput()
212
213
    Returns:
214
        pc : pcl.PointCloud
215
            The input pointcloud.
216
    """
217 1
    if same_as:
218 1
        if is_registered(same_as):
219 1
            pc.srs = same_as.srs.Clone()
220 1
            pc.offset = np.array(same_as.offset, dtype=np.float64)
221
    else:
222 1
        if type(srs) == type(osr.SpatialReference()):
223 1
            pc.srs = srs.Clone()
224 1
        elif srs is not None:
225 1
            pc.srs = osr.SpatialReference()
226 1
            pc.srs.SetFromUserInput(srs)
227
        else:
228
            pc.srs = osr.SpatialReference()
229
230 1
        if offset is not None:
231 1
            offset = np.asarray(offset, dtype=np.float64)
232 1
            if len(offset) != 3:
233
                raise TypeError("Offset should be an np.array([3])")
234
            else:
235 1
                pc.offset = offset
236
237
    return pc
238