Core Client¶
The SDK keeps the original protocol-native methods for callers that need exact server JSON, and adds typed wrappers for common early-adopter workflows.
Service Catalog¶
Use list_services() when you need the raw /rest/services payload:
from honua_sdk import HonuaClient
with HonuaClient("https://honua.example") as client:
payload = client.list_services()
Use list_service_summaries() when you want typed service entries:
from honua_sdk import HonuaClient
with HonuaClient("https://honua.example") as client:
services = client.list_service_summaries()
for service in services:
print(service.name, service.type)
Capability Discovery¶
Use capabilities() when you need the advertised data-plane protocols and
feature flags before choosing an optional surface. supports() checks a single
protocol or feature name:
from honua_sdk import HonuaClient
with HonuaClient("https://honua.example") as client:
capabilities = client.capabilities()
if capabilities.supports("stac"):
items = client.stac().items("imagery")
if client.supports("geoservices-feature-service"):
services = client.list_service_summaries()
Older servers that do not expose /api/v1/capabilities fall back to readiness
and GeoServices catalog discovery.
Shared Source Queries¶
For application code that wants the same shape across data protocols, prefer
the shared Source/Query/Result API. client.source(...) binds a
SourceDescriptor to a reusable facade; source.query() collects normalized
QueryFeature entries, and source.stream()/source.iter_features() streams
the same normalized features:
from honua_sdk import HonuaClient, Query, SourceDescriptor, SourceLocator
with HonuaClient("https://honua.example") as client:
parcels = client.source(
SourceDescriptor(
id="parcels",
protocol="geoservices-feature-service",
locator=SourceLocator(service_id="parcels", layer_id=0),
)
)
result = parcels.query(
Query(
where="status = 'active'",
out_fields=["objectid", "name", "status"],
pagination={"limit": 2000},
)
)
for feature in result.features:
print(feature.id, feature.properties, feature.geometry)
native = parcels.protocol()
metadata = native.layer_metadata(0)
The source facade uses canonical cross-SDK protocol ids such as
geoservices-feature-service, ogc-features, stac, and odata; common
aliases such as feature-server, featureserver, feature-service, and
ogc_api_features are accepted. The full alias table is exposed as
honua_sdk.PROTOCOL_ALIASES and normalized through
honua_sdk.normalize_protocol(...). Python uses snake_case for query fields
(out_fields, return_geometry, query_all()), while TypeScript and .NET use
their idiomatic casing.
SourceDescriptor.supports(...) reflects protocol-advertised capabilities;
source.supports(...) reflects the normalized operations this Python facade can
execute directly. Use source.protocol(...) for native protocol operations that
are advertised but not normalized by the shared facade.
The compact client-level helpers remain available for existing callers and for
one-off queries. client.query() returns a FeatureQueryResult; iter_query()
streams normalized features from FeatureServer, OGC API Features, STAC, or
OData:
from honua_sdk import FeatureQuery, HonuaClient
with HonuaClient("https://honua.example") as client:
ogc_features = client.query(
"parcels",
protocol="ogc-features",
filter="status = 'active'",
bbox=[-180, -90, 180, 90],
fields=["name", "status"],
limit=2000,
)
odata_features = client.query(
"4",
protocol="odata",
filter="Status eq 'active'",
fields=["ObjectId", "Name"],
limit=2000,
)
stac_items = client.query(
FeatureQuery(
source="imagery",
protocol="stac",
filter="eo:cloud_cover < 10",
bbox=[-180, -90, 180, 90],
limit=500,
)
)
Use the protocol-specific clients below when you need exact native payloads, server-specific query options, or endpoint metadata.
query_features() returns the raw FeatureServer response. query_feature_set()
wraps the same response in a FeatureSet with typed Feature entries:
Legacy typed shape.
Feature.attributesis the raw GeoServices key-value shape. The canonicalSource.query()shown above returnsQueryFeaturewith GeoJSON-stylefeature.properties. Usequery_feature_set()when you want the FeatureServer-shaped wrapper; prefer theSourcefacade in new code.
from honua_sdk import HonuaClient
with HonuaClient("https://honua.example") as client:
feature_set = client.query_feature_set(
service_id="parcels",
layer_id=0,
where="status = 'active'",
out_fields=["objectid", "name", "status"],
)
for feature in feature_set.features:
print(feature.object_id, feature.attributes["name"])
The raw response is preserved on FeatureSet.raw and Feature.raw for fields
that are not lifted into typed attributes.
Pagination¶
Use query_features_all() to collect a FeatureServer layer with resultOffset
and resultRecordCount. Use client.feature_server(service_id).query_pages()
or query_items() when you want to stream pages or individual typed features:
from honua_sdk import HonuaClient
with HonuaClient("https://honua.example") as client:
features = client.query_features_all(
service_id="parcels",
layer_id=0,
where="1=1",
page_size=500,
limit=2000,
)
feature_server = client.feature_server("parcels")
for page in feature_server.query_pages(layer_id=0, page_size=500, limit=2000):
print(len(page.features), page.exceeded_transfer_limit)
for feature in feature_server.query_items(layer_id=0, page_size=500, limit=2000):
print(feature.object_id)
The helper stops when:
- the requested
limitis reached - a page returns fewer features than requested
exceededTransferLimitis absent or falsemax_pagesis reached
Use max_pages as a guardrail for broad queries. Keep page_size aligned with
the server layer's configured maximum record count.
The async client exposes the same methods:
from honua_sdk import AsyncHonuaClient
async with AsyncHonuaClient("https://honua.example") as client:
features = await client.query_features_all(
service_id="parcels",
layer_id=0,
page_size=500,
)
async for feature in client.feature_server("parcels").query_items(layer_id=0, page_size=500):
print(feature.object_id)
Edits¶
apply_edits() returns raw JSON. apply_edits_result() wraps add, update, and
delete outcomes in typed operation results:
result = client.apply_edits_result(
service_id="parcels",
layer_id=0,
updates=[
{
"attributes": {"objectid": 10, "status": "retired"},
}
],
)
if not result.all_succeeded:
print(result.raw)
Errors And Retries¶
Non-2xx HTTP responses raise HonuaHttpError. The exception includes:
status_codemessagebody, when the server response body can be parsed or decoded
Transport failures such as DNS, TLS, or connection errors are normalized to
HonuaHttpError with status_code == 0.
Clients retry transient responses through the shared retry transport. Retry is
limited to 429, 502, and 503, with Retry-After support when the server
provides it. Auth failures such as 401 and 403 are not retried; refresh or
replace credentials before retrying those requests.