| 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.