Spatial#

class nominal.experimental.spatial.Spatial(rid: str, name: str, description: str | None, labels: Sequence[str], properties: Mapping[str, str], is_archived: bool, dagger_uuid: str, metadata: PointCloudMetadata, created_at: IntegralNanosecondsUTC, start_timestamp: IntegralNanosecondsUTC | None, end_timestamp: IntegralNanosecondsUTC | None)#

Bases: HasRid, RefreshableGrpcMixin[Spatial]

A spatial data source (e.g. a point cloud) tracked by Nominal.

rid: str#
name: str#
description: str | None#
labels: Sequence[str]#
properties: Mapping[str, str]#
is_archived: bool#
dagger_uuid: str#
metadata: PointCloudMetadata#
created_at: IntegralNanosecondsUTC#
start_timestamp: IntegralNanosecondsUTC | None#
end_timestamp: IntegralNanosecondsUTC | None#
add_point_cloud_csv(csv_path: Path | str, *, column_types: Mapping[str, Literal['int', 'real', 'string']] | None = None, rgb_column: str | None = None, rgb_attribute: str = 'color') → IngestionJob#
add_point_cloud_csv(csv_path: Path | str, *, column_types: Mapping[str, Literal['int', 'real', 'string']] | None = None, rgb_column: str | None = None, rgb_attribute: str = 'color', timestamp_column: str, timestamp_type: Relative) → IngestionJob

Upload a point-cloud CSV and add it to this spatial’s model.

The CSV must contain at minimum x, y, z columns (case-insensitive); remaining columns are classified as real or string by sampling the first ~1000 data rows. Numeric columns are always typed real – pass column_types to ask for int on a column you know holds integers.

The import runs asynchronously, so this returns as soon as the ingest is accepted, not when the point cloud is queryable. Poll the returned job to wait for it.

Pass timestamp_column and timestamp_type for a cloud that was captured or built over time. The column’s extent is measured and recorded on the spatial, which is what lets a workbook’s 3D panel drive the cloud from the playhead – without it the panel has no way to map playhead position onto per-point time and renders the whole cloud at once.

Parameters:
  • csv_path – Path to the point-cloud CSV to upload.

  • column_types – Per-column overrides for the real/string classifier. The only way to get the Int wire type, which is never inferred.

  • rgb_column – Name of a column holding a six-character hex colour (“rrggbb”, no leading #). It becomes an Rgb attribute, which is what per-point colouring reads; separate 0-255 columns cannot drive colour, and are silently skipped by the parser rather than rejected.

  • rgb_attribute – Name for the resulting attribute.

  • timestamp_column – Column holding per-point time. Measuring its extent costs one extra pass over the file, so it is only read when named here.

  • timestamp_type – How to read timestamp_column. Only Relative is accepted: per-point time has to be an offset from a start instant, because filtering happens on the GPU in f32, where the spacing between representable values at epoch magnitude is over two minutes. Its unit must be one the 3D panel can read per-point time in – nanoseconds, microseconds, milliseconds or seconds – since it re-reads the raw column values in a unit picked at view time and cannot name any other.

Returns:

The submitted ingest job. Follow it with status and refresh(), which is the only signal it carries: the import writes into this spatial’s own model rather than the catalog, so the job reports no dataset_rid, a produced_file_count of zero, and an empty dataset_files(). as_files_ingested() still raises if the job fails, but otherwise yields nothing – an empty result there means the job produced no catalog entry, not that it produced no points.

Raises:
  • FileNotFoundError – If csv_path does not exist.

  • TypeError – If timestamp_type is not a Relative.

  • ValueError – If only one of timestamp_column / timestamp_type is given, timestamp_type reads the column in a unit the 3D panel cannot, the CSV is empty or uses quoting, lacks x/y/z columns, column_types names a column or type that does not exist, rgb_column is not in the header, or timestamp_column is missing from the header or holds a non-numeric value.

archive() → None#

Archive this spatial, hiding it from search (reversible).

Raises:

NominalError – Error archiving the spatial via gRPC.

refresh() → Self#
unarchive() → None#

Unarchive a previously archived spatial.

Raises:

NominalError – Error unarchiving the spatial via gRPC.

update(*, name: str | None = None, description: str | None = None, properties: Mapping[str, str] | None = None, labels: Sequence[str] | None = None) → Self#

Replace spatial metadata in-place and return the updated spatial.

Fields set to None are left untouched; empty labels or properties clear them. With no fields supplied, refresh the local snapshot without changing metadata.

Raises:

NominalError – Error updating or refreshing spatial metadata via gRPC.