# Spatial

## class nominal.experimental.spatial.Spatial(rid, name, description, labels, properties, is_archived, dagger_uuid, metadata, created_at, start_timestamp, end_timestamp)

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*](https://docs.python.org/3/library/pathlib.html#pathlib.Path "(in Python v3.15)") *|* [*str*](https://docs.python.org/3/builtins/stdtypes.html#str "(in Python v3.15)"))

      Path to the point-cloud CSV to upload.
    - **column_types** ([*Mapping*](https://docs.python.org/3/library/typing.html#typing.Mapping "(in Python v3.15)")*[*[*str*](https://docs.python.org/3/builtins/stdtypes.html#str "(in Python v3.15)")*,* [*Literal*](https://docs.python.org/3/library/typing.html#typing.Literal "(in Python v3.15)")*[**'int'**,* *'real'**,* *'string'**]**]* *|* *None*)

      Per-column overrides for the real/string classifier. The only
      way to get the Int wire type, which is never inferred.
    - **rgb_column** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str "(in Python v3.15)") *|* *None*)

      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** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str "(in Python v3.15)"))

      Name for the resulting attribute.
    - **timestamp_column** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str "(in Python v3.15)") *|* *None*)

      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** ([*Relative*](https://dev.nominal.io/python/ref/generated/nominal.ts.Relative.md#nominal.ts.Relative "nominal.ts.Relative") *|* *None*)

      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()`](https://dev.nominal.io/python/ref/generated/nominal.experimental.spatial.Spatial.md#nominal.experimental.spatial.Spatial.refresh "nominal.experimental.spatial.Spatial.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**](https://docs.python.org/3/builtins/exceptions.html#FileNotFoundError "(in Python v3.15)") – If `csv_path` does not exist.
    - [**TypeError**](https://docs.python.org/3/builtins/exceptions.html#TypeError "(in Python v3.15)") – If `timestamp_type` is not a `Relative`.
    - [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError "(in Python v3.15)") – 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.

Return type:
:   [*IngestionJob*](https://dev.nominal.io/python/ref/generated/nominal.core.IngestionJob.md#nominal.core.IngestionJob "nominal.core.ingestion_job.IngestionJob")

### archive()

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

Raises:
:   [**NominalError**](https://dev.nominal.io/python/ref/generated/nominal.exceptions.NominalError.md#nominal.exceptions.NominalError "nominal.exceptions.NominalError") – Error archiving the spatial via gRPC.

### refresh()

Return type:
:   [*Self*](https://docs.python.org/3/library/typing.html#typing.Self "(in Python v3.15)")

### unarchive()

Unarchive a previously archived spatial.

Raises:
:   [**NominalError**](https://dev.nominal.io/python/ref/generated/nominal.exceptions.NominalError.md#nominal.exceptions.NominalError "nominal.exceptions.NominalError") – Error unarchiving the spatial via gRPC.

### update(*, name=None, description=None, properties=None, labels=None)

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**](https://dev.nominal.io/python/ref/generated/nominal.exceptions.NominalError.md#nominal.exceptions.NominalError "nominal.exceptions.NominalError") – Error updating or refreshing spatial metadata via gRPC.

Return type:
:   [*Self*](https://docs.python.org/3/library/typing.html#typing.Self "(in Python v3.15)")
