pystac-ext-pointcloud 1.0.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,605 @@
1
+ """Implements the :stac-ext:`Point Cloud Extension <pointcloud>`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from typing import (
7
+ Any,
8
+ Generic,
9
+ Literal,
10
+ TypeVar,
11
+ cast,
12
+ )
13
+
14
+ import pystac
15
+ from pystac.extensions.base import (
16
+ ExtensionManagementMixin,
17
+ PropertiesExtension,
18
+ SummariesExtension,
19
+ )
20
+ from pystac.extensions.hooks import ExtensionHooks
21
+ from pystac.summaries import RangeSummary
22
+ from pystac.utils import StringEnum, get_required, map_opt
23
+
24
+ #: Generalized version of :class:`~pystac.Item`, :class:`~pystac.Asset`,
25
+ #: or :class:`~pystac.ItemAssetDefinition`
26
+ T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition)
27
+
28
+ SCHEMA_URI: str = "https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json"
29
+ PREFIX: str = "pc:"
30
+
31
+ COUNT_PROP = PREFIX + "count"
32
+ TYPE_PROP = PREFIX + "type"
33
+ ENCODING_PROP = PREFIX + "encoding"
34
+ SCHEMAS_PROP = PREFIX + "schemas"
35
+ DENSITY_PROP = PREFIX + "density"
36
+ STATISTICS_PROP = PREFIX + "statistics"
37
+
38
+
39
+ class PhenomenologyType(StringEnum):
40
+ """Valid values for the ``pc:type`` field in the :stac-ext:`Pointcloud Item
41
+ Properties <pointcloud#item-properties>`."""
42
+
43
+ LIDAR = "lidar"
44
+ EOPC = "eopc"
45
+ RADAR = "radar"
46
+ SONAR = "sonar"
47
+ OTHER = "other"
48
+
49
+
50
+ class SchemaType(StringEnum):
51
+ """Valid values for the ``type`` field in a :stac-ext:`Schema Object
52
+ <pointcloud#schema-object>`."""
53
+
54
+ FLOATING = "floating"
55
+ UNSIGNED = "unsigned"
56
+ SIGNED = "signed"
57
+
58
+
59
+ class Schema:
60
+ """Defines a schema for dimension of a pointcloud (e.g., name, size, type)
61
+
62
+ Use :meth:`Schema.create` to create a new instance of ``Schema`` from
63
+ properties.
64
+ """
65
+
66
+ properties: dict[str, Any]
67
+
68
+ def __init__(self, properties: dict[str, Any]) -> None:
69
+ self.properties = properties
70
+
71
+ def apply(self, name: str, size: int, type: SchemaType) -> None:
72
+ """Sets the properties for this Schema.
73
+
74
+ Args:
75
+ name : The name of dimension.
76
+ size : The size of the dimension in bytes. Whole bytes are supported.
77
+ type : Dimension type. Valid values are ``floating``, ``unsigned``, and
78
+ ``signed``
79
+ """
80
+ self.properties["name"] = name
81
+ self.properties["size"] = size
82
+ self.properties["type"] = type
83
+
84
+ @classmethod
85
+ def create(cls, name: str, size: int, type: SchemaType) -> Schema:
86
+ """Creates a new Schema.
87
+
88
+ Args:
89
+ name : The name of dimension.
90
+ size : The size of the dimension in bytes. Whole bytes are supported.
91
+ type : Dimension type. Valid values are ``floating``, ``unsigned``, and
92
+ ``signed``
93
+ """
94
+ c = cls({})
95
+ c.apply(name=name, size=size, type=type)
96
+ return c
97
+
98
+ @property
99
+ def size(self) -> int:
100
+ """Gets or sets the size value."""
101
+ return cast(int, get_required(self.properties.get("size"), self, "size"))
102
+
103
+ @size.setter
104
+ def size(self, v: int) -> None:
105
+ if not isinstance(v, int):
106
+ raise pystac.STACError(f"size must be an int! Invalid input: {v}")
107
+
108
+ self.properties["size"] = v
109
+
110
+ @property
111
+ def name(self) -> str:
112
+ """Gets or sets the name property for this Schema."""
113
+ return cast(str, get_required(self.properties.get("name"), self, "name"))
114
+
115
+ @name.setter
116
+ def name(self, v: str) -> None:
117
+ self.properties["name"] = v
118
+
119
+ @property
120
+ def type(self) -> SchemaType:
121
+ """Gets or sets the type property. Valid values are ``floating``, ``unsigned``,
122
+ and ``signed``."""
123
+ return cast(SchemaType, get_required(self.properties.get("type"), self, "type"))
124
+
125
+ @type.setter
126
+ def type(self, v: SchemaType) -> None:
127
+ self.properties["type"] = v
128
+
129
+ def __repr__(self) -> str:
130
+ return "<Schema name={} size={} type={}>".format(
131
+ self.properties.get("name"),
132
+ self.properties.get("size"),
133
+ self.properties.get("type"),
134
+ )
135
+
136
+ def to_dict(self) -> dict[str, Any]:
137
+ """Returns this schema as a dictionary."""
138
+ return self.properties
139
+
140
+
141
+ class Statistic:
142
+ """Defines a single statistic for Pointcloud channel or dimension
143
+
144
+ Use :meth:`Statistic.create` to create a new instance of
145
+ ``Statistic`` from property values."""
146
+
147
+ properties: dict[str, Any]
148
+
149
+ def __init__(self, properties: dict[str, Any]) -> None:
150
+ self.properties = properties
151
+
152
+ def apply(
153
+ self,
154
+ name: str,
155
+ position: int | None = None,
156
+ average: float | None = None,
157
+ count: int | None = None,
158
+ maximum: float | None = None,
159
+ minimum: float | None = None,
160
+ stddev: float | None = None,
161
+ variance: float | None = None,
162
+ ) -> None:
163
+ """Sets the properties for this Statistic.
164
+
165
+ Args:
166
+ name : REQUIRED. The name of the channel.
167
+ position : Optional position of the channel in the schema.
168
+ average : Optional average of the channel.
169
+ count : Optional number of elements in the channel.
170
+ maximum : Optional maximum value of the channel.
171
+ minimum : Optional minimum value of the channel.
172
+ stddev : Optional standard deviation of the channel.
173
+ variance : Optional variance of the channel.
174
+ """
175
+ self.properties["name"] = name
176
+ self.properties["position"] = position
177
+ self.properties["average"] = average
178
+ self.properties["count"] = count
179
+ self.properties["maximum"] = maximum
180
+ self.properties["minimum"] = minimum
181
+ self.properties["stddev"] = stddev
182
+ self.properties["variance"] = variance
183
+
184
+ @classmethod
185
+ def create(
186
+ cls,
187
+ name: str,
188
+ position: int | None = None,
189
+ average: float | None = None,
190
+ count: int | None = None,
191
+ maximum: float | None = None,
192
+ minimum: float | None = None,
193
+ stddev: float | None = None,
194
+ variance: float | None = None,
195
+ ) -> Statistic:
196
+ """Creates a new Statistic class.
197
+
198
+ Args:
199
+ name : REQUIRED. The name of the channel.
200
+ position : Optional position of the channel in the schema.
201
+ average : Optional average of the channel.
202
+ count : Optional number of elements in the channel.
203
+ maximum : Optional maximum value of the channel.
204
+ minimum : Optional minimum value of the channel.
205
+ stddev : Optional standard deviation of the channel.
206
+ variance : Optional variance of the channel.
207
+ """
208
+ c = cls({})
209
+ c.apply(
210
+ name=name,
211
+ position=position,
212
+ average=average,
213
+ count=count,
214
+ maximum=maximum,
215
+ minimum=minimum,
216
+ stddev=stddev,
217
+ variance=variance,
218
+ )
219
+ return c
220
+
221
+ @property
222
+ def name(self) -> str:
223
+ """Gets or sets the name property."""
224
+ return cast(str, get_required(self.properties.get("name"), self, "name"))
225
+
226
+ @name.setter
227
+ def name(self, v: str) -> None:
228
+ if v is not None:
229
+ self.properties["name"] = v
230
+ else:
231
+ self.properties.pop("name", None)
232
+
233
+ @property
234
+ def position(self) -> int | None:
235
+ """Gets or sets the position property."""
236
+ return self.properties.get("position")
237
+
238
+ @position.setter
239
+ def position(self, v: int | None) -> None:
240
+ if v is not None:
241
+ self.properties["position"] = v
242
+ else:
243
+ self.properties.pop("position", None)
244
+
245
+ @property
246
+ def average(self) -> float | None:
247
+ """Gets or sets the average property."""
248
+ return self.properties.get("average")
249
+
250
+ @average.setter
251
+ def average(self, v: float | None) -> None:
252
+ if v is not None:
253
+ self.properties["average"] = v
254
+ else:
255
+ self.properties.pop("average", None)
256
+
257
+ @property
258
+ def count(self) -> int | None:
259
+ """Gets or sets the count property."""
260
+ return self.properties.get("count")
261
+
262
+ @count.setter
263
+ def count(self, v: int | None) -> None:
264
+ if v is not None:
265
+ self.properties["count"] = v
266
+ else:
267
+ self.properties.pop("count", None)
268
+
269
+ @property
270
+ def maximum(self) -> float | None:
271
+ """Gets or sets the maximum property."""
272
+ return self.properties.get("maximum")
273
+
274
+ @maximum.setter
275
+ def maximum(self, v: float | None) -> None:
276
+ if v is not None:
277
+ self.properties["maximum"] = v
278
+ else:
279
+ self.properties.pop("maximum", None)
280
+
281
+ @property
282
+ def minimum(self) -> float | None:
283
+ """Gets or sets the minimum property."""
284
+ return self.properties.get("minimum")
285
+
286
+ @minimum.setter
287
+ def minimum(self, v: float | None) -> None:
288
+ if v is not None:
289
+ self.properties["minimum"] = v
290
+ else:
291
+ self.properties.pop("minimum", None)
292
+
293
+ @property
294
+ def stddev(self) -> float | None:
295
+ """Gets or sets the stddev property."""
296
+ return self.properties.get("stddev")
297
+
298
+ @stddev.setter
299
+ def stddev(self, v: float | None) -> None:
300
+ if v is not None:
301
+ self.properties["stddev"] = v
302
+ else:
303
+ self.properties.pop("stddev", None)
304
+
305
+ @property
306
+ def variance(self) -> float | None:
307
+ """Gets or sets the variance property."""
308
+ return self.properties.get("variance")
309
+
310
+ @variance.setter
311
+ def variance(self, v: float | None) -> None:
312
+ if v is not None:
313
+ self.properties["variance"] = v
314
+ else:
315
+ self.properties.pop("variance", None)
316
+
317
+ def __repr__(self) -> str:
318
+ return f"<Statistic statistics={str(self.properties)}>"
319
+
320
+ def to_dict(self) -> dict[str, Any]:
321
+ """Returns this statistic as a dictionary."""
322
+ return self.properties
323
+
324
+ def __eq__(self, o: object) -> bool:
325
+ if not isinstance(o, Statistic):
326
+ return NotImplemented
327
+ return self.to_dict() == o.to_dict()
328
+
329
+
330
+ class PointcloudExtension(
331
+ Generic[T],
332
+ PropertiesExtension,
333
+ ExtensionManagementMixin[pystac.Item | pystac.Collection],
334
+ ):
335
+ """An abstract class that can be used to extend the properties of an
336
+ :class:`~pystac.Item` or :class:`~pystac.Asset` with properties from the
337
+ :stac-ext:`Point Cloud Extension <pointcloud>`. This class is generic over the type
338
+ of STAC Object to be extended (e.g. :class:`~pystac.Item`,
339
+ :class:`~pystac.Asset`).
340
+
341
+ To create a concrete instance of :class:`PointcloudExtension`, use the
342
+ :meth:`PointcloudExtension.ext` method. For example:
343
+
344
+ .. code-block:: python
345
+
346
+ >>> item: pystac.Item = ...
347
+ >>> pc_ext = PointcloudExtension.ext(item)
348
+ """
349
+
350
+ name: Literal["pc"] = "pc"
351
+
352
+ def apply(
353
+ self,
354
+ count: int,
355
+ type: PhenomenologyType | str,
356
+ encoding: str,
357
+ schemas: list[Schema],
358
+ density: float | None = None,
359
+ statistics: list[Statistic] | None = None,
360
+ ) -> None:
361
+ """Applies Pointcloud extension properties to the extended Item.
362
+
363
+ Args:
364
+ count : REQUIRED. The number of points in the cloud.
365
+ type : REQUIRED. Phenomenology type for the point cloud. Possible valid
366
+ values might include lidar, eopc, radar, sonar, or otherThe type of file
367
+ or data format of the cloud.
368
+ encoding : REQUIRED. Content encoding or format of the data.
369
+ schemas : REQUIRED. A sequential array of items
370
+ that define the dimensions and their types.
371
+ density : Number of points per square unit area.
372
+ statistics : A sequential array of items mapping to
373
+ pc:schemas defines per-channel statistics.
374
+ """
375
+ self.count = count
376
+ self.type = type
377
+ self.encoding = encoding
378
+ self.schemas = schemas
379
+ self.density = density
380
+ self.statistics = statistics
381
+
382
+ @property
383
+ def count(self) -> int:
384
+ """Gets or sets the number of points in the Item."""
385
+ return get_required(self._get_property(COUNT_PROP, int), self, COUNT_PROP)
386
+
387
+ @count.setter
388
+ def count(self, v: int) -> None:
389
+ self._set_property(COUNT_PROP, v, pop_if_none=False)
390
+
391
+ @property
392
+ def type(self) -> PhenomenologyType | str:
393
+ """Gets or sets the phenomenology type for the point cloud."""
394
+ return get_required(self._get_property(TYPE_PROP, str), self, TYPE_PROP)
395
+
396
+ @type.setter
397
+ def type(self, v: PhenomenologyType | str) -> None:
398
+ self._set_property(TYPE_PROP, v, pop_if_none=False)
399
+
400
+ @property
401
+ def encoding(self) -> str:
402
+ """Gets or sets the content encoding or format of the data."""
403
+ return get_required(self._get_property(ENCODING_PROP, str), self, ENCODING_PROP)
404
+
405
+ @encoding.setter
406
+ def encoding(self, v: str) -> None:
407
+ self._set_property(ENCODING_PROP, v, pop_if_none=False)
408
+
409
+ @property
410
+ def schemas(self) -> list[Schema]:
411
+ """Gets or sets the list of :class:`Schema` instances defining
412
+ dimensions and types for the data.
413
+ """
414
+ result = get_required(
415
+ self._get_property(SCHEMAS_PROP, list[dict[str, Any]]), self, SCHEMAS_PROP
416
+ )
417
+ return [Schema(s) for s in result]
418
+
419
+ @schemas.setter
420
+ def schemas(self, v: list[Schema]) -> None:
421
+ self._set_property(SCHEMAS_PROP, [x.to_dict() for x in v], pop_if_none=False)
422
+
423
+ @property
424
+ def density(self) -> float | None:
425
+ """Gets or sets the number of points per square unit area."""
426
+ return self._get_property(DENSITY_PROP, float)
427
+
428
+ @density.setter
429
+ def density(self, v: float | None) -> None:
430
+ self._set_property(DENSITY_PROP, v)
431
+
432
+ @property
433
+ def statistics(self) -> list[Statistic] | None:
434
+ """Gets or sets the list of :class:`Statistic` instances describing
435
+ the pre-channel statistics. Elements in this list map to elements in the
436
+ :attr:`PointcloudExtension.schemas` list."""
437
+ result = self._get_property(STATISTICS_PROP, list[dict[str, Any]])
438
+ return map_opt(lambda stats: [Statistic(s) for s in stats], result)
439
+
440
+ @statistics.setter
441
+ def statistics(self, v: list[Statistic] | None) -> None:
442
+ set_value = map_opt(lambda stats: [s.to_dict() for s in stats], v)
443
+ self._set_property(STATISTICS_PROP, set_value)
444
+
445
+ @classmethod
446
+ def get_schema_uri(cls) -> str:
447
+ return SCHEMA_URI
448
+
449
+ @classmethod
450
+ def ext(cls, obj: T, add_if_missing: bool = False) -> PointcloudExtension[T]:
451
+ """Extends the given STAC Object with properties from the :stac-ext:`Point Cloud
452
+ Extension <pointcloud>`.
453
+
454
+ This extension can be applied to instances of :class:`~pystac.Item` or
455
+ :class:`~pystac.Asset`.
456
+
457
+ Raises:
458
+
459
+ pystac.ExtensionTypeError : If an invalid object type is passed.
460
+ """
461
+ if isinstance(obj, pystac.Item):
462
+ cls.ensure_has_extension(obj, add_if_missing)
463
+ return cast(PointcloudExtension[T], ItemPointcloudExtension(obj))
464
+ elif isinstance(obj, pystac.Asset):
465
+ if obj.owner is not None and not isinstance(obj.owner, pystac.Item):
466
+ raise pystac.ExtensionTypeError(
467
+ "Pointcloud extension does not apply to Collection Assets."
468
+ )
469
+ cls.ensure_owner_has_extension(obj, add_if_missing)
470
+ return cast(PointcloudExtension[T], AssetPointcloudExtension(obj))
471
+ elif isinstance(obj, pystac.ItemAssetDefinition):
472
+ cls.ensure_owner_has_extension(obj, add_if_missing)
473
+ return cast(PointcloudExtension[T], ItemAssetsPointcloudExtension(obj))
474
+ else:
475
+ raise pystac.ExtensionTypeError(cls._ext_error_message(obj))
476
+
477
+ @classmethod
478
+ def summaries(
479
+ cls, obj: pystac.Collection, add_if_missing: bool = False
480
+ ) -> SummariesPointcloudExtension:
481
+ cls.ensure_has_extension(obj, add_if_missing)
482
+ return SummariesPointcloudExtension(obj)
483
+
484
+
485
+ class ItemPointcloudExtension(PointcloudExtension[pystac.Item]):
486
+ """A concrete implementation of :class:`PointcloudExtension` on an
487
+ :class:`~pystac.Item` that extends the properties of the Item to include
488
+ properties defined in the :stac-ext:`Point Cloud Extension <pointcloud>`.
489
+
490
+ This class should generally not be instantiated directly. Instead, call
491
+ :meth:`PointcloudExtension.ext` on an :class:`~pystac.Item` to extend it.
492
+ """
493
+
494
+ item: pystac.Item
495
+ properties: dict[str, Any]
496
+
497
+ def __init__(self, item: pystac.Item):
498
+ self.item = item
499
+ self.properties = item.properties
500
+
501
+ def __repr__(self) -> str:
502
+ return f"<ItemPointcloudExtension Item id={self.item.id}>"
503
+
504
+
505
+ class AssetPointcloudExtension(PointcloudExtension[pystac.Asset]):
506
+ """A concrete implementation of :class:`PointcloudExtension` on an
507
+ :class:`~pystac.Asset` that extends the Asset fields to include properties defined
508
+ in the :stac-ext:`Point Cloud Extension <pointcloud>`.
509
+
510
+ This class should generally not be instantiated directly. Instead, call
511
+ :meth:`PointcloudExtension.ext` on an :class:`~pystac.Asset` to extend it.
512
+ """
513
+
514
+ asset_href: str
515
+ """The ``href`` value of the :class:`~pystac.Asset` being extended."""
516
+
517
+ properties: dict[str, Any]
518
+ """The :class:`~pystac.Asset` fields, including extension properties."""
519
+
520
+ additional_read_properties: Iterable[dict[str, Any]] | None = None
521
+ """If present, this will be a list containing 1 dictionary representing the
522
+ properties of the owning :class:`~pystac.Item`."""
523
+
524
+ def __init__(self, asset: pystac.Asset):
525
+ self.asset_href = asset.href
526
+ self.properties = asset.extra_fields
527
+ if asset.owner and isinstance(asset.owner, pystac.Item):
528
+ self.additional_read_properties = [asset.owner.properties]
529
+ self.repr_id = f"href={asset.href} item.id={asset.owner.id}"
530
+ else:
531
+ self.repr_id = f"href={asset.href}"
532
+
533
+ def __repr__(self) -> str:
534
+ return f"<AssetPointcloudExtension Asset {self.repr_id}>"
535
+
536
+
537
+ class ItemAssetsPointcloudExtension(PointcloudExtension[pystac.ItemAssetDefinition]):
538
+ properties: dict[str, Any]
539
+ asset_defn: pystac.ItemAssetDefinition
540
+
541
+ def __init__(self, item_asset: pystac.ItemAssetDefinition):
542
+ self.asset_defn = item_asset
543
+ self.properties = item_asset.properties
544
+
545
+
546
+ class SummariesPointcloudExtension(SummariesExtension):
547
+ """A concrete implementation of :class:`~pystac.extensions.base.SummariesExtension`
548
+ that extends the ``summaries`` field of a :class:`~pystac.Collection` to include
549
+ properties defined in the :stac-ext:`Point Cloud Extension <pointcloud>`.
550
+ """
551
+
552
+ @property
553
+ def count(self) -> RangeSummary[int] | None:
554
+ return self.summaries.get_range(COUNT_PROP)
555
+
556
+ @count.setter
557
+ def count(self, v: RangeSummary[int] | None) -> None:
558
+ self._set_summary(COUNT_PROP, v)
559
+
560
+ @property
561
+ def type(self) -> list[PhenomenologyType | str] | None:
562
+ return self.summaries.get_list(TYPE_PROP)
563
+
564
+ @type.setter
565
+ def type(self, v: list[PhenomenologyType | str] | None) -> None:
566
+ self._set_summary(TYPE_PROP, v)
567
+
568
+ @property
569
+ def encoding(self) -> list[str] | None:
570
+ return self.summaries.get_list(ENCODING_PROP)
571
+
572
+ @encoding.setter
573
+ def encoding(self, v: list[str] | None) -> None:
574
+ self._set_summary(ENCODING_PROP, v)
575
+
576
+ @property
577
+ def density(self) -> RangeSummary[float] | None:
578
+ return self.summaries.get_range(DENSITY_PROP)
579
+
580
+ @density.setter
581
+ def density(self, v: RangeSummary[float] | None) -> None:
582
+ self._set_summary(DENSITY_PROP, v)
583
+
584
+ @property
585
+ def statistics(self) -> list[Statistic] | None:
586
+ return map_opt(
587
+ lambda stats: [Statistic(d) for d in stats],
588
+ self.summaries.get_list(STATISTICS_PROP),
589
+ )
590
+
591
+ @statistics.setter
592
+ def statistics(self, v: list[Statistic] | None) -> None:
593
+ self._set_summary(
594
+ STATISTICS_PROP,
595
+ map_opt(lambda stats: [s.to_dict() for s in stats], v),
596
+ )
597
+
598
+
599
+ class PointcloudExtensionHooks(ExtensionHooks):
600
+ schema_uri: str = SCHEMA_URI
601
+ prev_extension_ids = {"pointcloud"}
602
+ stac_object_types = {pystac.STACObjectType.ITEM}
603
+
604
+
605
+ POINTCLOUD_EXTENSION_HOOKS: ExtensionHooks = PointcloudExtensionHooks()
File without changes
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: pystac-ext-pointcloud
3
+ Version: 1.0.0rc0
4
+ Summary: Point Cloud extension for PySTAC
5
+ Project-URL: Documentation, https://pystac.readthedocs.io
6
+ Project-URL: Repository, https://github.com/stac-utils/pystac
7
+ Project-URL: Issues, https://github.com/stac-utils/pystac/issues
8
+ Project-URL: Changelog, https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md
9
+ Project-URL: Discussions, https://github.com/radiantearth/stac-spec/discussions/categories/stac-software
10
+ License: Apache-2.0
11
+ Keywords: STAC,catalog,imagery,pointcloud,pystac,raster
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Natural Language :: English
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: pystac-core
23
+ Description-Content-Type: text/markdown
24
+
25
+ # pystac-ext-pointcloud
26
+
27
+ [PySTAC](https://pypi.org/project/pystac/) extension package for the [Point Cloud Extension](https://github.com/stac-extensions/pointcloud).
28
+ This extension provides fields for describing point cloud data, including point count, encoding, density, schemas, and statistics.
29
+
30
+ ## Supported versions
31
+
32
+ - [v1.0.0](https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json)
33
+
34
+ ## Versioning
35
+
36
+ This package's version corresponds to the version of the extension specification it targets.
37
+ When we release updates to the package code without changing the target extension version, we use [post releases](https://packaging.python.org/en/latest/discussions/versioning/#post-releases), e.g. `1.0.0.post1`.
@@ -0,0 +1,5 @@
1
+ pystac/extensions/pointcloud.py,sha256=Kt0i8gWV1YF0dC9BXqP06ATOrQJh59YvXDpjjKQRY7o,20413
2
+ pystac/extensions/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pystac_ext_pointcloud-1.0.0rc0.dist-info/METADATA,sha256=0F9Q0mZz6TGiBXLiHhyxzvsN40U-AIoA2N7vKOB6Pe8,1826
4
+ pystac_ext_pointcloud-1.0.0rc0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ pystac_ext_pointcloud-1.0.0rc0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any