Elaborazione e monitoraggio
Dopo che i file di input sono stati caricati, la piattaforma può generare output elaborati (nuvole di punti standardizzate, mesh HLOD, photosphere e così via). Questa guida spiega come elencare le opzioni di elaborazione, avviare l’elaborazione e monitorare i progressi finché ogni output raggiunge uno stato terminale.
Presuppone che tu abbia un bundle con almeno una sessione di caricamento completata e disponga degli scope read:bundle e write:bundle.
Come funziona
Sezione intitolata “Come funziona”L’elaborazione è fire-and-forget: l’API accoda un job di elaborazione e ritorna immediatamente.
sequenceDiagram
participant Client as Client
participant RCAPI as RCAPI
Client->>RCAPI: GET processing/options
RCAPI-->>Client: options + metadata
Client->>RCAPI: POST processing
RCAPI-->>Client: accepted or typed error
loop until all terminal
Client->>RCAPI: GET bundle/processings
RCAPI-->>Client: processing records (status, progress)
end
Endpoint
Sezione intitolata “Endpoint”| Metodo | Percorso | Scope | Scopo |
|---|---|---|---|
GET | /v1/bundles/{bundleId}/processing/options | read:bundle | Elencare i possibili tipi di output e i loro metadati |
POST | /v1/bundles/{bundleId}/processing | write:bundle | Avviare l’elaborazione per gli output richiesti |
GET | /v1/bundles/{bundleId}/processings | read:bundle | Elencare i record di elaborazione del bundle per monitorare i progressi |
GET | /v1/site/{siteId}/bundles/{bundleId} | read:hierarchy | Ottenere il bundle con i suoi componenti di output elaborati (link firmati) |
Passaggio 1: elencare le opzioni di elaborazione
Sezione intitolata “Passaggio 1: elencare le opzioni di elaborazione”Prima di avviare, scopri quali output il bundle può produrre e quanto costa ciascuno.
GET {api_url}/v1/bundles/{bundleId}/processing/options?page=1&limit=20&sortBy=type&sortDir=ASCAuthorization: Bearer {access_token}Parametri di query
Sezione intitolata “Parametri di query”| Parametro | Tipo | Descrizione |
|---|---|---|
page | number | Indice di pagina in base 1 (predefinito 1) |
limit | number | Dimensione della pagina (predefinito 20, massimo 20) |
sortBy | string | type o cost (predefinito type) |
sortDir | string | ASC o DESC (predefinito ASC) |
La risposta è un elenco paginato ({ items, total }). Ogni opzione descrive un tipo di output:
| Campo | Significato |
|---|---|
type | Tipo di componente di output da passare all’avvio dell’elaborazione (ad esempio StandardizedPointCloud, OgcPointCloudHlod) |
hasProcessing | true quando è già stata avviata un’elaborazione per questo output |
cost | Costo di elaborazione in byte per gli output a pagamento; 0 per gli output del piano gratuito |
Esempio:
{ "items": [ { "type": "StandardizedPointCloud", "hasProcessing": false, "cost": 734003200 }, { "type": "OgcPointCloudHlod", "hasProcessing": false, "cost": 734003200 } ], "total": 2}Salta gli output in cui hasProcessing è true a meno che tu non intenda rielaborare.
Passaggio 2: avviare l’elaborazione
Sezione intitolata “Passaggio 2: avviare l’elaborazione”Passa l’elenco dei tipi di componente di output che vuoi che la pipeline produca, più un campo cost che conferma l’addebito totale dell’elaborazione. Il server somma i valori cost del Passaggio 1 per ogni tipo in requestedComponents e rifiuta la richiesta quando la tua conferma non corrisponde.
L’elaborazione inizia in modo asincrono una volta accettato il costo.
POST {api_url}/v1/bundles/{bundleId}/processingAuthorization: Bearer {access_token}Content-Type: application/json
{ "requestedComponents": [ "StandardizedPointCloud", "OgcPointCloudHlod" ], "cost": 1468006400}Nell’esempio precedente, cost è 734003200 + 734003200 dall’elenco delle opzioni del Passaggio 1.
Risposta di successo
Sezione intitolata “Risposta di successo”204 No Content: l’elaborazione è stata accettata e accodata.
Risposta di errore
Sezione intitolata “Risposta di errore”Quando l’elaborazione non può essere avviata, l’API restituisce 409 Conflict con un error tipizzato:
Valore di error | Significato |
|---|---|
ProcessingCostMismatch | Il cost inviato non corrisponde al totale calcolato per gli output richiesti |
InsufficientProcessingCapacity | Il limite di elaborazione dell’organizzazione verrebbe superato |
NoInputDataFoundForProcessing | Nessun file di input finalizzato è disponibile sul bundle |
FailedToLaunchProcessing | L’invio alla pipeline non è riuscito per un motivo imprevisto |
Esempio:
{ "error": "ProcessingCostMismatch"}La validazione strutturale del batch caricato (tipi obbligatori, estensioni, dipendenze) viene applicata dalla pipeline di elaborazione dopo l’avvio. Se la tua sessione di caricamento è stata completata ma ha violato il contratto dei requirements, l’elaborazione potrebbe fallire in seguito anche se ogni file è stato caricato correttamente e la chiamata di avvio ha restituito 204.
Passaggio 3: monitorare i progressi
Sezione intitolata “Passaggio 3: monitorare i progressi”Dopo un avvio riuscito, effettua il polling dei record di elaborazione del bundle finché ognuno raggiunge uno stato terminale.
GET {api_url}/v1/bundles/{bundleId}/processingsAuthorization: Bearer {access_token}La risposta è un elenco ({ items, total }) con ogni record di elaborazione del bundle. Non ci sono parametri di query; tutti i record vengono restituiti in un’unica chiamata.
Ogni elemento traccia un job di elaborazione:
| Campo | Significato |
|---|---|
id | Identificatore del record di elaborazione |
status | Stato della pipeline (vedi la tabella sotto) |
progress | Percentuale di completamento (0–100) mentre è attivo |
createdAt | Quando il job è stato creato |
updatedAt | Quando il job ha cambiato stato l’ultima volta |
launchedById | Id dell’utente che ha avviato il job, oppure null |
knownErrors | Errori strutturati, ciascuno con un errorCode e i errorFilepaths interessati (null quando non è specifico di un file) |
Stati di elaborazione
Sezione intitolata “Stati di elaborazione”| Stato | Terminale? | Significato |
|---|---|---|
Pending | no | In coda, non ancora avviato |
Processing | no | La pipeline è in esecuzione |
Restarted | no | Il job è stato riavviato ed è nuovamente in esecuzione |
Ready | sì | Output prodotto con successo |
Failed | sì | Errore non strutturato della pipeline |
FailedToLaunch | sì | Il job non è mai entrato nella pipeline |
KnownError | sì | Errore strutturato di validazione o di formato |
Effettua il polling finché ogni record non si trova in uno stato terminale (Ready, Failed, FailedToLaunch o KnownError).
Esempio di risposta durante l’esecuzione:
{ "items": [ { "id": "3f2b1c4d-8e7a-4b2c-9d1e-0a1b3c4d4e6f", "status": "Processing", "progress": 42, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:12:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a2b2c1d", "knownErrors": [] }, { "id": "9c0d1e2f-3a4b-5c6d-7e8f-9a0b2c3d3e4f", "status": "Pending", "progress": 0, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:05:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-9c0d-1e2f3a3b1c1d", "knownErrors": [] } ], "total": 2}Esempio con un errore noto:
{ "items": [ { "id": "3f2b1c4d-8e7a-4b2c-9d1e-0a2b3c4d4e5f", "status": "KnownError", "progress": 100, "createdAt": "2026-06-01T14:05:00Z", "updatedAt": "2026-06-01T14:48:00Z", "launchedById": "b1a2c3d4-5e6f-7a8b-1c0d-2e6f3a4b5c6d", "knownErrors": [ { "errorCode": "MissingTrajectory", "errorFilepaths": ["scan.las"] } ] } ], "total": 1}L’elenco delle elaborazioni non include il type dell’output per record. Per ricollegare gli output completati ai loro tipi di componente e ai link firmati, recupera i componenti del bundle (vedi Recupero degli output elaborati).
Indicazioni per il polling
Sezione intitolata “Indicazioni per il polling”- Intervallo: 1-5 minuti è un valore predefinito ragionevole. L’elaborazione può durare da diversi minuti a diverse ore a seconda della dimensione dell’input.
- Limiti di frequenza: gli endpoint del bundle condividono un bucket di rate limit con diversi altri endpoint. Evita il polling con frequenza inferiore al minuto.
Recupero degli output elaborati
Sezione intitolata “Recupero degli output elaborati”Una volta che un record di elaborazione raggiunge lo stato Ready, recupera il bundle per ottenere i suoi componenti di output e i relativi link di download firmati. I dettagli del bundle risiedono sull’endpoint della gerarchia:
GET {api_url}/v1/site/{siteId}/bundles/{bundleId}Authorization: Bearer {access_token}Questo endpoint richiede lo scope read:hierarchy e restituisce il bundle con il suo array components:
| Campo | Significato |
|---|---|
id | Id del bundle |
name | Nome del bundle |
components | Una voce per ogni componente disponibile (input e output elaborati) |
Ogni componente contiene:
| Campo | Significato |
|---|---|
id | Id del componente |
type | Tipo di componente, ad esempio StandardizedPointCloud, OgcPointCloudHlod |
version | Versione del componente |
signedLink | URL firmato per scaricare il componente; nessun header di autenticazione richiesto |
roots | Percorsi del punto di ingresso all’interno del componente, quando applicabile |
Esempio:
{ "id": "a6f75b3c-f261-4b27-9a2a-9a6cc4178a13", "name": "ZF capture 2026-06-01", "components": [ { "id": "5d6e7f8a-9b0c-1d2e-3f4a-1b3c7d4e8f0a", "type": "StandardizedPointCloud", "version": "1", "signedLink": "https://cdn…/components/…?X-Amz-…" } ]}Un progetto RealityPlan espone lo stesso bundle tramite GET /v1/reality-plan/{projectId}/bundles/{bundleId} (scope read:twin).
Esempio completo
Sezione intitolata “Esempio completo”import timeimport requests
API_URL = "https://<your-regional-api-url>"TOKEN = "<access_token>"BUNDLE_ID = "<bundleId>"
headers = {"Authorization": f"Bearer {TOKEN}"}base = f"{API_URL}/v1/bundles/{BUNDLE_ID}"
# 1. Scopri gli output.options = requests.get( f"{base}/processing/options", headers=headers, params={"page": 1, "limit": 20},).json()
options_by_type = {item["type"]: item for item in options["items"]}to_process = [ item["type"] for item in options["items"] if not item["hasProcessing"]]total_cost = sum(options_by_type[t]["cost"] for t in to_process)print(f"Launching: {to_process} (cost: {total_cost})")
# 2. Avvia l'elaborazione.response = requests.post( f"{base}/processing", headers=headers, json={"requestedComponents": to_process, "cost": total_cost},)
if response.status_code == 409: raise RuntimeError( f"Processing failed to start: {response.json().get('error')}" )response.raise_for_status() # expect 204
# 3. Effettua il polling finché ogni job non è terminale.TERMINAL = {"Ready", "Failed", "FailedToLaunch", "KnownError"}POLL_SECONDS = 15TIMEOUT_SECONDS = 3600deadline = time.time() + TIMEOUT_SECONDS
while time.time() < deadline: listing = requests.get( f"{base}/processings", headers=headers, ).json()
processings = listing.get("items", []) for p in processings: print(f" {p['id']}: {p['status']} ({p['progress']}%)")
if processings and all(p["status"] in TERMINAL for p in processings): break
time.sleep(POLL_SECONDS)else: raise TimeoutError("Processing did not finish within the timeout")Passaggio successivo
Sezione intitolata “Passaggio successivo”Per recuperare i file di input grezzi che sono stati caricati (per archiviazione, rielaborazione o audit), consulta Scaricare i file di input.