콘텐츠로 이동

처리 및 모니터링

입력 파일이 업로드된 후, 플랫폼은 처리된 출력물(표준화된 포인트 클라우드, 메시 HLOD, 포토스피어 등)을 생성할 수 있습니다. 이 가이드에서는 처리 옵션을 나열하고, 처리를 시작하고, 모든 출력물이 종료 상태에 도달할 때까지 진행 상황을 폴링하는 방법을 설명합니다.

완료된 업로드 세션이 하나 이상 있는 번들을 보유하고 있으며 read:bundlewrite:bundle 스코프를 가지고 있다고 가정합니다.


처리는 실행 후 잊기(fire-and-forget) 방식입니다. API는 처리 작업을 대기열에 넣고 즉시 반환합니다.

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
메서드경로스코프목적
GET/v1/bundles/{bundleId}/processing/optionsread:bundle가능한 출력 유형과 메타데이터 나열
POST/v1/bundles/{bundleId}/processingwrite:bundle요청한 출력물에 대한 처리 시작
GET/v1/bundles/{bundleId}/processingsread:bundle진행 상황을 폴링하기 위해 번들의 처리 레코드 나열
GET/v1/site/{siteId}/bundles/{bundleId}read:hierarchy처리된 출력 컴포넌트(서명된 링크)와 함께 번들 가져오기

시작하기 전에 번들이 생성할 수 있는 출력물과 각각의 비용을 확인하세요.

GET {api_url}/v1/bundles/{bundleId}/processing/options?page=1&limit=20&sortBy=type&sortDir=ASC
Authorization: Bearer {access_token}
매개변수유형설명
pagenumber1부터 시작하는 페이지 인덱스(기본값 1)
limitnumber페이지 크기(기본값 20, 최대 20)
sortBystringtype 또는 cost(기본값 type)
sortDirstringASC 또는 DESC(기본값 ASC)

응답은 페이지 단위 목록({ items, total })입니다. 각 옵션은 하나의 출력 유형을 설명합니다.

필드의미
type처리를 시작할 때 전달할 출력 컴포넌트 유형(예: StandardizedPointCloud, OgcPointCloudHlod)
hasProcessing이 출력에 대한 처리가 이미 시작된 경우 true
cost유료 출력의 바이트 단위 처리 비용. 무료 등급 출력의 경우 0

예:

{
"items": [
{
"type": "StandardizedPointCloud",
"hasProcessing": false,
"cost": 734003200
},
{
"type": "OgcPointCloudHlod",
"hasProcessing": false,
"cost": 734003200
}
],
"total": 2
}

재처리할 의도가 아니라면 hasProcessingtrue인 출력물은 건너뛰세요.

파이프라인이 생성하기를 원하는 출력 컴포넌트 유형 목록과, 총 처리 요금을 확인하는 cost 필드를 전달합니다. 서버는 requestedComponents의 모든 유형에 대해 1단계의 cost 값을 합산하고, 확인 값이 일치하지 않으면 요청을 거부합니다.

비용이 수락되면 처리가 비동기적으로 시작됩니다.

POST {api_url}/v1/bundles/{bundleId}/processing
Authorization: Bearer {access_token}
Content-Type: application/json
{
"requestedComponents": [
"StandardizedPointCloud",
"OgcPointCloudHlod"
],
"cost": 1468006400
}

위 예제에서 cost는 1단계 옵션 목록의 734003200 + 734003200입니다.

204 No Content: 처리가 수락되어 대기열에 추가되었습니다.

처리를 시작할 수 없을 때, API는 유형이 지정된 error와 함께 409 Conflict를 반환합니다.

error의미
ProcessingCostMismatch제출된 cost가 요청한 출력물에 대해 계산된 총계와 일치하지 않음
InsufficientProcessingCapacity조직 처리 한도를 초과함
NoInputDataFoundForProcessing번들에 마무리된 입력 파일이 없음
FailedToLaunchProcessing예상치 못한 이유로 파이프라인 제출 실패

예:

{
"error": "ProcessingCostMismatch"
}

업로드된 배치의 구조적 검증(필수 유형, 확장자, 종속성)은 시작 후 처리 파이프라인에서 시행됩니다. 업로드 세션이 완료되었지만 requirements 계약을 위반한 경우, 모든 파일이 성공적으로 업로드되고 시작 호출이 204를 반환했더라도 나중에 처리가 실패할 수 있습니다.

성공적으로 시작한 후, 번들의 모든 처리 레코드가 종료 상태에 도달할 때까지 폴링합니다.

GET {api_url}/v1/bundles/{bundleId}/processings
Authorization: Bearer {access_token}

응답은 번들의 모든 처리 레코드가 있는 목록({ items, total })입니다. 쿼리 매개변수는 없습니다. 모든 레코드가 한 번의 호출로 반환됩니다.

각 항목은 하나의 처리 작업을 추적합니다.

필드의미
id처리 레코드의 식별자
status파이프라인 상태(아래 표 참조)
progress활성 상태일 때의 완료 백분율(0100)
createdAt작업이 생성된 시점
updatedAt작업이 마지막으로 상태를 변경한 시점
launchedById작업을 시작한 사용자의 id 또는 null
knownErrors구조화된 실패. 각각 errorCode와 영향을 받은 errorFilepaths가 있음(파일별이 아닐 때 null)
상태종료?의미
Pending아니오대기열에 있으며 아직 시작되지 않음
Processing아니오파이프라인이 실행 중
Restarted아니오작업이 다시 시작되어 재실행 중
Ready출력물이 성공적으로 생성됨
Failed구조화되지 않은 파이프라인 실패
FailedToLaunch작업이 파이프라인에 진입하지 못함
KnownError구조화된 검증 또는 형식 오류

모든 레코드가 종료 상태(Ready, Failed, FailedToLaunch, KnownError)가 될 때까지 폴링하세요.

진행 중 응답 예:

{
"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
}

알려진 오류가 있는 예:

{
"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
}

처리 목록에는 레코드별 출력 type이 포함되지 않습니다. 완료된 출력물을 해당 컴포넌트 유형 및 서명된 링크에 다시 매핑하려면 번들의 컴포넌트를 가져오세요(처리된 출력물 가져오기 참조).

  • 간격: 1~5분이 합리적인 기본값입니다. 처리는 입력 크기에 따라 수 분에서 수 시간까지 실행될 수 있습니다.
  • 속도 제한: 번들 엔드포인트는 여러 다른 엔드포인트와 속도 제한 버킷을 공유합니다. 1분 미만의 폴링은 피하세요.

처리 레코드가 Ready에 도달하면, 출력 컴포넌트와 서명된 다운로드 링크를 가져오기 위해 번들을 조회하세요. 번들 세부 정보는 계층 구조 엔드포인트에 있습니다.

GET {api_url}/v1/site/{siteId}/bundles/{bundleId}
Authorization: Bearer {access_token}

이 엔드포인트에는 read:hierarchy 스코프가 필요하며 components 배열과 함께 번들을 반환합니다.

필드의미
id번들 id
name번들 이름
components사용 가능한 컴포넌트당 하나의 항목(입력 및 처리된 출력)

각 컴포넌트에는 다음이 있습니다.

필드의미
id컴포넌트 id
type컴포넌트 유형(예: StandardizedPointCloud, OgcPointCloudHlod)
version컴포넌트 버전
signedLink컴포넌트를 다운로드하기 위한 서명된 URL. 인증 헤더 불필요
roots해당되는 경우 컴포넌트 내의 진입점 경로

예:

{
"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-…"
}
]
}

RealityPlan 프로젝트는 GET /v1/reality-plan/{projectId}/bundles/{bundleId}(스코프 read:twin)를 통해 동일한 번들을 노출합니다.

import time
import 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. Discover outputs.
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. Start processing.
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. Poll until every job is terminal.
TERMINAL = {"Ready", "Failed", "FailedToLaunch", "KnownError"}
POLL_SECONDS = 15
TIMEOUT_SECONDS = 3600
deadline = 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")

업로드된 원시 입력 파일을 검색하려면(보관, 재처리, 감사 목적) 입력 파일 다운로드를 참조하세요.