2
/******************************************************************************
5
* Project: libLAS - http://liblas.org - A BSD library for LAS format data.
6
* Purpose: Python Point implementation
7
* Author: Howard Butler, hobu.inc@gmail.com
9
******************************************************************************
10
* Copyright (c) 2009, Howard Butler
12
* All rights reserved.
14
* Redistribution and use in source and binary forms, with or without
15
* modification, are permitted provided that the following
18
* * Redistributions of source code must retain the above copyright
19
* notice, this list of conditions and the following disclaimer.
20
* * Redistributions in binary form must reproduce the above copyright
21
* notice, this list of conditions and the following disclaimer in
22
* the documentation and/or other materials provided
23
* with the distribution.
24
* * Neither the name of the Martin Isenburg or Iowa Department
25
* of Natural Resources nor the names of its contributors may be
26
* used to endorse or promote products derived from this software
27
* without specific prior written permission.
29
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
33
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
35
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
36
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
37
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
38
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
39
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
41
****************************************************************************/
54
def __init__(self, owned=True, handle=None, copy=False):
55
"""Instantiates a :obj:`liblas.point.Point` object. If you are \
56
creating and working with your own instances, you should normally \
57
not need to use any of the parameters.
60
A flag to denote whether or not the point owns itself and can
61
destroy itself upon going out of scope. There are instances where
62
:obj:`liblas.file.File` may actually own the point as it is read
63
from or written to files.
65
:param handle: ctypes object reference
66
A handle to an existing ctypes LASPointH object reference. Only
67
internal liblas code should have to worry about using this.
70
A flag to denote whether or not to copy the point upon creation.
71
Used in coordination with the :obj:`handle` parameter, and it
72
should not normally be used.
74
:rtype: :obj:`liblas.point.Point` object
78
self.handle = core.las.LASPoint_Copy(handle)
84
self.handle = core.las.LASPoint_Create()
89
if self.handle and core:
90
core.las.LASPoint_Destroy(self.handle)
93
return core.las.LASPoint_GetX(self.handle)
95
def set_x(self, value):
96
"""Sets the X coordinate of the LAS point to a floating point
100
The point will be descaled according to the :obj:`liblas.point.Point.header`'s
101
scale value for the X dimension.
104
return core.las.LASPoint_SetX(self.handle, value)
106
doc = """X coordinate of the LAS point as a double (scale applied).
109
Use obj:`liblas.point.Point.raw_x` if you want the unscaled ``x`` data.
113
return core.las.LASPoint_GetRawX(self.handle)
115
def set_raw_x(self, value):
116
"""Sets the X coordinate of the LAS point to an integer value
120
The point will be scaled according to the obj:`liblas.point.Point.header`'s
121
scale value for the X dimension when returned as a double obj:`liblas.point.Point.x`.
123
return core.las.LASPoint_SetRawX(self.handle, value)
125
doc = """The raw X coordinate of the point without its header's scaling
129
Use obj:`liblas.point.Point.x` if you want the scaled ``x`` data.
131
raw_x = property(get_raw_x, set_raw_x, None, doc)
133
x = property(get_x, set_x, None, doc)
136
return core.las.LASPoint_GetY(self.handle)
138
def set_y(self, value):
139
"""Sets the Y coordinate of the LAS point to a floating point
143
The point will be descaled according to the :obj:`liblas.point.Point.header`'s
144
scale value for the Y dimension.
146
return core.las.LASPoint_SetY(self.handle, value)
148
doc = """Y coordinate of the LAS point as a double (scale applied).
151
Use obj:`liblas.point.Point.raw_y` if you want the unscaled ``y`` data.
154
y = property(get_y, set_y, None, doc)
157
return core.las.LASPoint_GetRawY(self.handle)
159
def set_raw_y(self, value):
160
"""Sets the Y coordinate of the LAS point to an integer value
164
The point will be scaled according to the obj:`liblas.point.Point.header`'s
165
scale value for the Y dimension when returned as a double obj:`liblas.point.Point.y`.
167
return core.las.LASPoint_SetRawY(self.handle, value)
169
doc = """The raw Y coordinate of the point without its header's scaling
173
Use obj:`liblas.point.Point.y` if you want the scaled ``y`` data.
175
raw_y = property(get_raw_y, set_raw_y, None, doc)
178
return core.las.LASPoint_GetZ(self.handle)
180
def set_z(self, value):
181
"""Sets the Z coordinate of the LAS point to a floating point
185
The point will be descaled according to the obj:`liblas.point.Point.header`'s
186
scale value for the Z dimension.
188
return core.las.LASPoint_SetZ(self.handle, value)
190
doc = """Z coordinate of the LAS point as a double (scale applied).
193
Use obj:`liblas.point.Point.raw_z` if you want the unscaled ``z`` data.
195
z = property(get_z, set_z, None, doc)
198
return core.las.LASPoint_GetRawZ(self.handle)
200
def set_raw_z(self, value):
201
"""Sets the Z coordinate of the LAS point to an integer value
205
The point will be scaled according to the obj:`liblas.point.Point.header`'s
206
scale value for the Z dimension when returned as a double obj:`liblas.point.Point.y`.
208
return core.las.LASPoint_SetRawZ(self.handle, value)
210
doc = """The raw Z coordinate of the point without its header's scaling
214
Use obj:`liblas.point.Point.z` if you want the scaled ``z`` data.
216
raw_z = property(get_raw_z, set_raw_z, None, doc)
218
def get_return_number(self):
219
"""Returns the return number of the point"""
220
return core.las.LASPoint_GetReturnNumber(self.handle)
222
def set_return_number(self, value):
223
"""Sets the return number of the point to an integer"""
224
core.las.LASPoint_SetReturnNumber(self.handle, value)
225
doc = """The pulse return number for a given laser pulse.
227
From the specification_:
229
The Return Number is the pulse return number for a given output pulse.
230
A given output laser pulse can have many returns, and they must be
231
marked in sequence of return. The first return will have a Return
232
Number of one, the second a Return Number of two, and so on up to five
235
return_number = property(get_return_number, set_return_number, None, doc)
237
def get_number_of_returns(self):
238
"""Returns the number of returns for the point"""
239
return core.las.LASPoint_GetNumberOfReturns(self.handle)
241
def set_number_of_returns(self, value):
242
"""Sets the number of returns for the point"""
243
core.las.LASPoint_SetNumberOfReturns(self.handle, value)
244
doc = """The number of returns for a given laser pulse.
246
From the specification_:
248
The Number of Returns is the total number of returns for a given
249
pulse. For example, a laser data point may be return two (Return
250
Number) within a total number of five returns.
252
number_of_returns = property(get_number_of_returns,
253
set_number_of_returns,
257
def get_scan_direction(self):
258
"""Returns the scan direction for the point"""
259
return core.las.LASPoint_GetScanDirection(self.handle)
261
def set_scan_direction(self, value):
262
"""Sets the scan direction as an integer for the point"""
263
core.las.LASPoint_SetScanDirection(self.handle, value)
264
doc = """Scan direction for the point
266
From the specification_:
268
The Scan Direction Flag denotes the direction at which the scanner
269
mirror was traveling at the time of the output pulse. A bit value of 1
270
is a positive scan direction, and a bit value of 0 is a negative scan
271
direction (where positive scan direction is a scan moving from the
272
left side of the in-track direction to the right side and negative the
276
scan_direction = property(get_scan_direction,
281
def get_flightline_edge(self):
282
"""Denotes whether the point is a flight line edge"""
283
return core.las.LASPoint_GetFlightLineEdge(self.handle)
285
def set_flightline_edge(self, value):
286
"""Sets the flightline edge as an integer for the point. Must be 0
287
(not an edge) or 1 (an edge)"""
288
core.las.LASPoint_SetFlightLineEdge(self.handle, value)
289
doc = """Flightline edge flag for the point
291
From the specification_:
293
The Edge of Flight Line data bit has a value of 1 only when the point
294
is at the end of a scan. It is the last point on a given scan line
295
before it changes direction.
297
flightline_edge = property(get_flightline_edge,
302
def get_scan_flags(self):
303
"""Returns the raw scan flags for the point.
304
See the LAS 1.0 or 1.1 specification for information how to interpret
306
return core.las.LASPoint_GetScanFlags(self.handle)
308
def set_scan_flags(self, value):
309
"""Sets the raw scan flags for the point. See the LAS 1.0 or 1.1
310
specification for information how to interpret or use the convenience
311
functions like flightline_edge, scan_direction, etc.
313
core.las.LASPoint_SetScanFlags(self.handle, value)
314
doc = """Scan flags for the point. This is a combination of the
315
:obj:`flightline_edge` \ :obj:`return_number` :obj:`number_of_returns` and
316
:obj:`scan_direction`. Use the individual methods for setting these values
319
scan_flags = property(get_scan_flags, set_scan_flags, None, doc)
321
def get_classification(self):
322
return core.las.LASPoint_GetClassification(self.handle)
324
def set_classification(self, value):
325
core.las.LASPoint_SetClassification(self.handle, value)
326
doc = """The point's classification as a raw byte value.
328
The following large section of information is verboten from the
329
specification_ for your convenience:
331
This filed represents the "class" attributes of a point. If a point
332
has never been classified, this byte must be set to zero. There are no
333
user defined classes since all point formats 0 supply 8 bits per point
334
for user defined operations.
336
.. csv-table:: Classification Bit Field Encoding
337
:header: "Bits", "Field Name", "Description"
340
0:4, "Classification", "Standard ASPRS classification as defined
341
in the following classification table."
342
5, "Synthetic", "If set then this point was created by a technique
343
other than LIDAR collection such as digitized from a
344
photogrammetric stereo model or by traversing a waveform."
345
6, "Key-point", "If set, this point is considered to be a model
346
key- point and thus generally should not be withheld in a thinning
348
7, "Witheld", "If set, this point should not be included in
349
processing (synonymous with Deleted)."
352
Bits 5, 6 and 7 are treated as flags and can be set or clear in
353
any combination. For example, a point with bits 5 and 6 both set
354
to one and the lower five bits set to 2 (see table below) would be
355
a ground point that had been Synthetically collected and marked as
358
.. csv-table:: ASPRS Standard LiDAR Point Classes
359
:header: "Classification", "Meaning"
362
0, "Created, never classified"
366
4, "Medium Vegetation"
369
7, "Low Point (noise)"
370
8, "Model Key-point (mass point)"
372
10, "Reserved for ASPRS Definition"
373
11, "Reserved for ASPRS Definition"
375
13-31, "Reserved for ASPRS Definition"
378
We are using both 0 and 1 as Unclassified to maintain
379
compatibility with current popular classification software such as
380
TerraScan. We extend the idea of classification value 1 to include
381
cases in which data have been subjected to a classification
382
algorithm but emerged in an undefined state. For example, data
383
with class 0 is sent through an algorithm to detect man-made
384
structures -- points that emerge without having been assigned as
385
belonging to structures could be remapped from class 0 to class 1
388
Overlap Points are those points that were immediately culled
389
during the merging of overlapping flight lines. In general, the
390
Withheld bit should be set since these points are not subsequently
394
classification = property(get_classification,
399
def get_scan_angle_rank(self):
400
"""Returns the scan angle rank of the point. It will be between -90
402
return core.las.LASPoint_GetScanAngleRank(self.handle)
404
def set_scan_angle_rank(self, value):
405
"""Sets the scan angle rank of the point. It must be between -90 and
407
core.las.LASPoint_SetScanAngleRank(self.handle, value)
409
doc = """Scan angle of the point.
411
From the specification_:
412
The Scan Angle Rank is a signed one-byte number with a valid range
413
from -90 to +90. The Scan Angle Rank is the angle (rounded to the
414
nearest integer in the absolute value sense) at which the laser point
415
was output from the laser system including the roll of the aircraft.
416
The scan angle is within 1 degree of accuracy from +90 to -90 degrees.
417
The scan angle is an angle based on 0 degrees being nadir, and -90
418
degrees to the left side of the aircraft in the direction of flight.
420
scan_angle = property(get_scan_angle_rank, set_scan_angle_rank, None, doc)
422
def get_user_data(self):
423
return core.las.LASPoint_GetUserData(self.handle)
425
def set_user_data(self, value):
426
core.las.LASPoint_SetUserData(self.handle, value)
427
doc = """User Data for the point. This is a single byte of data and \
428
and can be anything the software wants to attach to the point"""
429
user_data = property(get_user_data, set_user_data, None, doc)
431
def get_point_source_id(self):
432
return core.las.LASPoint_GetPointSourceId(self.handle)
434
def set_point_source_id(self, value):
435
core.las.LASPoint_SetUserData(self.handle, value)
436
doc = """Point Source ID for the point.
438
From the specification_:
440
This value indicates the file from which this point originated. Valid
441
values for this field are 1 to 65,535 inclusive with zero being used
442
for a special case discussed below. The numerical value corresponds to
443
the File Source ID from which this point originated. Zero is reserved
444
as a convenience to system implementers. A Point Source ID of zero
445
implies that this point originated in this file. This implies that
446
processing software should set the Point Source ID equal to the File
447
Source ID of the file containing this point at some time during
450
point_source_id = property(get_user_data, set_user_data, None, doc)
452
def get_intensity(self):
453
return core.las.LASPoint_GetIntensity(self.handle)
455
def set_intensity(self, value):
456
core.las.LASPoint_SetIntensity(self.handle, value)
457
doc = """Intensity value as an short integer for the point
459
From the specification_:
461
The intensity value is the integer representation of the pulse return
462
magnitude. This value is optional and system specific. However, it
463
should always be included if available.
465
intensity = property(get_intensity, set_intensity, None, doc)
468
t = core.las.LASPoint_GetTime(self.handle)
469
floor = math.floor(t)
470
ms = float(t) - floor
473
ms = int(round(ms * 1000000))
478
value = datetime.datetime(lt[0], lt[1], lt[2],
479
lt[3], lt[4], lt[5], ms)
482
def set_time(self, value):
483
t = time.mktime(value.timetuple())
485
ms = value.microsecond
486
t = float(t) + ms * 0.000001
487
core.las.LASPoint_SetTime(self.handle, t)
489
doc = """Interpeted (:class:`datetime.datetime` instance) time value for
494
>>> td = datetime.timedelta(hours=6) # my timezone is -6
495
>>> t = datetime.datetime(2008,3,19) - td
498
datetime.datetime(2008, 3, 19, 0, 0)
501
Because no header is available, no coordination is done with
502
respect to the allowable LAS time types. You must coordinate with
503
the :obj:`liblas.header.Header.global_encoding` to determine the
504
actual time value for the point. If you want to merely preserve
505
existing point data, use :obj:`liblas.point.Point.raw_time`
508
time = property(get_time, set_time, None, doc)
510
def get_raw_time(self):
511
t = core.las.LASPoint_GetTime(self.handle)
514
def set_raw_time(self, value):
515
core.las.LASPoint_SetTime(self.handle, value)
517
doc = """Uninterpeted time value for the point.
520
Because no header is available, no coordination is done with
521
respect to the allowable LAS time types. You must coordinate with
522
the :obj:`liblas.header.Header.global_encoding` to determine the
523
actual time value for the point. See the ASPRS LAS 1.1-1.3
524
specifications for more detail.
526
raw_time = property(get_raw_time, set_raw_time, None, doc)
529
return color.Color(handle=core.las.LASPoint_GetColor(self.handle))
531
def set_color(self, value):
532
return core.las.LASPoint_SetColor(self.handle, value.handle)
533
doc = """Raw color value for the point as an :obj:`liblas.color.Color`
537
RGB values should always be normalized to 16 bit values. For example,
538
when encoding an 8 bit per channel pixel, multiply each channel value
539
by 256 prior to storage in these fields. This normalization allows
540
color values from different camera bit depths to be accurately merged.
543
color = property(get_color, set_color, None, doc)
545
def get_header(self):
546
return header.Header(handle=core.las.LASPoint_GetHeader(self.handle))
548
def set_header(self, value):
549
return core.las.LASPoint_SetHeader(self.handle, value.handle)
550
header = property(get_header, set_header, None, None)
554
return core.las.LASPoint_GetXML(self.handle)
556
xml = property(get_xml, None, None, None)
560
length = self.header.data_record_length
561
d = (ctypes.c_ubyte*length)()
562
d2 = ctypes.cast(d, ctypes.POINTER(ctypes.c_ubyte))
563
core.las.LASPoint_GetData(self.handle, d2)
566
def set_data(self, data):
567
d = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte))
569
core.las.LASPoint_SetData(self.handle, d, len(data))
571
doc = """Raw data for the point. Shoot yourself in the foot if you must!
573
data = property(get_data, set_data, None, doc)