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
|
|||
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 |
This check looks for lines that are too long. You can specify the maximum line length.