spatialdds 0.2.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spatialdds-0.2.0 → spatialdds-0.3.1}/PKG-INFO +104 -17
- {spatialdds-0.2.0 → spatialdds-0.3.1}/README.md +103 -16
- {spatialdds-0.2.0 → spatialdds-0.3.1}/pyproject.toml +1 -1
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/__init__.py +7 -5
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/client.py +41 -3
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/records.py +228 -35
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/PKG-INFO +104 -17
- {spatialdds-0.2.0 → spatialdds-0.3.1}/setup.cfg +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/idl/core.idl +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/idl/discovery.idl +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/idl/types.idl +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/builtin_Time.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetRequest.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetResponse.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_argeo_QualityRequirements.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_common_KV.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_common_Vec2.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_Aabb3.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_Edge.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_Node.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_PlannedTrajectory.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_SnapshotResponse.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_TileKey.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_Announce.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_ContentAnnounce.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_CoverageElement.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_CoverageResponse.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_Depart.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_KV.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_Transform.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_owm_Entity.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_Detection2D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_Detection2DSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_Detection3D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_Detection3DSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrackSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_semantics_KeypointLink.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameHeader.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameQuality.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_Linspace.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIReply.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIRequest.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_common_StreamMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetection.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetectionSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarFrame.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetection.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetectionSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadSensorMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorFrame.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioObservation.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioScan.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioSensorMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamArraySet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamFrame.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_CamIntrinsics.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_Keypoint2D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_Track2D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionDetections.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionFrame.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionMeta.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_CameraInfo.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_Feature2D.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_FeatureMatch.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_KeyframeFeatures.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_Landmark.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_MatchSet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_TrackObs.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_slam_frontend_Tracklet.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_vio_ImuInfo.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_vio_MagnetometerSample.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/schemas/spatial_vio_SensorFusionSample.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/artifacts/serializer_tables.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/canonical.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/canonjson.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/assets/duck.gltf +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/assets/fountain-label.txt +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/assets/fountain-marker.gltf +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/fountain-anchors.mcap +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/manifests/content-fountain-label.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/demo/manifests/content-fountain-marker.json +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/errors.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/serve.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/__init__.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/gateway.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/gw_http.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/idlmodel.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/pin.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/publish.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/webjson.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds/server/xcdr2.py +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/SOURCES.txt +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/dependency_links.txt +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/entry_points.txt +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/requires.txt +0 -0
- {spatialdds-0.2.0 → spatialdds-0.3.1}/spatialdds.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialdds
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command.
|
|
5
5
|
Author: Open AR Cloud
|
|
6
6
|
License: Apache-2.0
|
|
@@ -43,6 +43,10 @@ not have to assemble member by member.
|
|
|
43
43
|
specification versions move independently; `spatialdds.SPEC_COMMIT` is the one
|
|
44
44
|
that says what the wire looks like.
|
|
45
45
|
|
|
46
|
+
The source, the reference gateway, the recordings and every gate behind the
|
|
47
|
+
claims on this page are at [https://github.com/OpenArCloud/spatialdds-web](https://github.com/OpenArCloud/spatialdds-web). Every code block below is executed
|
|
48
|
+
by one of those gates on every run, against an endpoint started for it.
|
|
49
|
+
|
|
46
50
|
## Your own registry, in one command
|
|
47
51
|
|
|
48
52
|
```sh
|
|
@@ -72,34 +76,101 @@ Appendix N's schemas make every member of a record required, so an `Announce`
|
|
|
72
76
|
is eighteen members, a `ContentAnnounce` sixteen, a `GeoAnchor` nine. The
|
|
73
77
|
builders take the arguments you actually have:
|
|
74
78
|
|
|
79
|
+
<!-- bar: verbs -->
|
|
75
80
|
```python
|
|
76
81
|
import spatialdds as sd
|
|
82
|
+
ep = sd.connect("http://127.0.0.1:8787", token="not-a-secret", scene="mytown")
|
|
83
|
+
a = ep.publish_service(service_id="mytown-vps", kind="VPS",
|
|
84
|
+
coverage=sd.circle(30.2849, -97.7393, 500))
|
|
85
|
+
b = ep.publish_content(
|
|
86
|
+
content_id="duck", title="A duck", position=(30.2849, -97.7393),
|
|
87
|
+
asset=sd.asset_ref("https://cdn.example/duck.gltf",
|
|
88
|
+
media_type="model/gltf+json"))
|
|
89
|
+
c = ep.publish_anchor("anchor/duck-perch", lat=30.2849, lon=-97.7393,
|
|
90
|
+
method="VisualFix", sigma_enu_m=(0.25, 0.25, 0.5),
|
|
91
|
+
content="duck")
|
|
92
|
+
print("service", a.created, "| content", b.created, "| anchor", c.created)
|
|
93
|
+
print("manifest", b.manifest.doc["id"])
|
|
94
|
+
```
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
ep.publish_content(content_id="duck", title="Duck", position=(30.2849, -97.7393),
|
|
82
|
-
asset="https://cdn.example/duck.gltf", sha256=digest)
|
|
83
|
-
ep.publish_anchor("anchor/duck-perch", lat=30.2849, lon=-97.7393,
|
|
84
|
-
method="VisualFix", sigma_enu_m=(0.25, 0.25, 0.5),
|
|
85
|
-
content="duck")
|
|
96
|
+
```console
|
|
97
|
+
service True | content True | anchor True
|
|
98
|
+
manifest spatialdds://demo.spatialdds.org/mytown/content/duck
|
|
86
99
|
```
|
|
87
100
|
|
|
88
101
|
`publish_service` and `publish_content` are two PUTs each -- the manifest
|
|
89
102
|
first, then the record that points at it -- and return what `publish` returns,
|
|
90
103
|
with the manifest's own result on `.manifest`.
|
|
91
104
|
|
|
105
|
+
A full service contract is the same one call, with the claims stated:
|
|
106
|
+
|
|
107
|
+
<!-- bar: contract -->
|
|
108
|
+
```python
|
|
109
|
+
import spatialdds as sd
|
|
110
|
+
ep = sd.connect("http://127.0.0.1:8787", token="not-a-secret", scene="mytown")
|
|
111
|
+
area = sd.circle(30.2849, -97.7393, 500)
|
|
112
|
+
w = ep.publish_service(
|
|
113
|
+
service_id="mytown-vps", kind="VPS", coverage=area,
|
|
114
|
+
caps=sd.capabilities(
|
|
115
|
+
supported=[sd.profile("spatial.core", minor=8)],
|
|
116
|
+
features=["web-binding/json"]),
|
|
117
|
+
manifest=sd.service_manifest(
|
|
118
|
+
"mytown-vps", "VPS", authority=ep.authority, scene="mytown",
|
|
119
|
+
profiles=["Core", "AR+Geo"],
|
|
120
|
+
request={"features_topic": "feat.keyframe"},
|
|
121
|
+
response={"minimal": "geo.fix"},
|
|
122
|
+
limits={"max_fps": 10},
|
|
123
|
+
auth={"scheme": "bearer",
|
|
124
|
+
"issuer": "https://registry.mytown.example"},
|
|
125
|
+
coverage=area, cells=sd.cells(area)))
|
|
126
|
+
print("profiles", w.manifest.doc["profiles"])
|
|
127
|
+
print("limits ", w.manifest.doc["limits"])
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```console
|
|
131
|
+
profiles ['Core', 'AR+Geo']
|
|
132
|
+
limits {'max_fps': 10}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Every one of those is something a service operator knows and a library cannot
|
|
136
|
+
guess. [The mytown walkthrough](https://github.com/OpenArCloud/spatialdds-web/blob/main/docs/MYTOWN.md) publishes that
|
|
137
|
+
and prints the whole served document;
|
|
138
|
+
[`gates/builders.py`](https://github.com/OpenArCloud/spatialdds-web/blob/main/gates/builders.py) rebuilds that
|
|
139
|
+
repository's hand-authored `austin-vps` and duck records through these calls
|
|
140
|
+
and compares them member by member, which is what makes "every member is
|
|
141
|
+
reachable from an argument" a check rather than a claim.
|
|
142
|
+
|
|
143
|
+
**Two forms for an asset, because there are two situations.**
|
|
144
|
+
`sd.asset(path_or_bytes, media_type=...)` reads the data and computes the hash
|
|
145
|
+
and the byte count itself: a hash nobody typed is a hash a reader can trust.
|
|
146
|
+
`sd.asset_ref(url, hash=..., media_type=...)` points at something somebody
|
|
147
|
+
else hosts, and the hash comes from whoever has the bytes. Pass no hash and
|
|
148
|
+
the reference carries no integrity claim, which costs a reader one thing:
|
|
149
|
+
`fetch_asset()` raises rather than reporting a check it could not perform
|
|
150
|
+
([finding 31](https://github.com/OpenArCloud/spatialdds-web/blob/main/WEB-BINDING-FINDINGS.md)).
|
|
151
|
+
`publish_content` takes either, or a path, or bytes, or a URL.
|
|
152
|
+
|
|
92
153
|
The builders are also usable on their own: `sd.service_announce`,
|
|
93
|
-
`sd.
|
|
94
|
-
`sd.
|
|
154
|
+
`sd.service_manifest`, `sd.content_announce`, `sd.content_manifest`,
|
|
155
|
+
`sd.geo_anchor`, with `sd.circle`, `sd.bbox`, `sd.geopose`, `sd.cov_pos3`,
|
|
156
|
+
`sd.cov_none`, `sd.profile`, `sd.capabilities`, `sd.asset_ref`,
|
|
95
157
|
`sd.frame`,
|
|
96
158
|
`sd.earth_fixed`, `sd.stamp`, `sd.topic`, `sd.asset`, `sd.cells`,
|
|
97
159
|
`sd.service_uri` and `sd.content_uri`. Each returns a plain dict in canonical
|
|
98
160
|
form, publishable as it stands and dumpable as the authoring JSON this
|
|
99
161
|
repository's tools read. `spatialdds.records` is the module they live in.
|
|
100
162
|
|
|
101
|
-
**
|
|
102
|
-
|
|
163
|
+
**Conventions fill plumbing. They never fill claims.** A stamp of now, an
|
|
164
|
+
earth-fixed frame, a checksum computed from the record itself: plumbing, with
|
|
165
|
+
one sensible value each, and filling them saves you writing out what you had
|
|
166
|
+
no choice about. A profile list, a rate limit, a request topic, an auth
|
|
167
|
+
scheme, a title: claims, and no default for any of them is honest. State
|
|
168
|
+
nothing and you publish a minimal record, which is *correct*; state a
|
|
169
|
+
contract and you publish it in one call. There is nothing in between for a
|
|
170
|
+
library to invent.
|
|
171
|
+
|
|
172
|
+
**What they fill in, and what they refuse to.** The conventions above are
|
|
173
|
+
documented where they are applied: a stamp of now unless you pass `when=`, an
|
|
103
174
|
earth-fixed frame unless you pass one, and URIs assembled under a stated
|
|
104
175
|
authority -- never a guessed one. `ep.publish_*` gets the authority from the
|
|
105
176
|
endpoint itself (`ep.authority`, which asks the resolver metadata, then the
|
|
@@ -160,7 +231,7 @@ the latched set is complete.
|
|
|
160
231
|
```python
|
|
161
232
|
import spatialdds as sd
|
|
162
233
|
ep = sd.connect("http://127.0.0.1:8787")
|
|
163
|
-
svc = ep.search(geohash="9v6krc")
|
|
234
|
+
svc = next(h for h in ep.search(geohash="9v6krc") if h.serves("geo_anchor"))
|
|
164
235
|
for ev in svc.topic("geo_anchor").subscribe(timeout=10):
|
|
165
236
|
if isinstance(ev, sd.EndOfReplay): print("latched set complete:", ev.replayed); break
|
|
166
237
|
print(" ", ev.key, ev.data["method"], ev.data["geopose"]["stamp"]["sec"])
|
|
@@ -213,7 +284,7 @@ call goes away and the bar is one line.
|
|
|
213
284
|
|
|
214
285
|
## The surface
|
|
215
286
|
|
|
216
|
-
| | |
|
|
287
|
+
| call | what it does |
|
|
217
288
|
| --- | --- |
|
|
218
289
|
| `sd.connect(base, token=None, verify_integrity=True, timeout=15.0, lifted=True, scene="default", authority=None)` | an `Endpoint`; nothing is fetched until you ask |
|
|
219
290
|
| `ep.search(geohash=None, kinds=None)` | §3.3.0 search → a list of `Hit` |
|
|
@@ -227,6 +298,7 @@ call goes away and the bar is one line.
|
|
|
227
298
|
| `ep.publish_anchor(anchor_id, lat=, lon=, method=, content=None, ...)` | build and PUT an anchor, optionally naming content |
|
|
228
299
|
| `ep.withdraw(topic=, key=)` | DELETE: leaves the latched set and disposes |
|
|
229
300
|
| `hit.service_id`, `.kind`, `.coverage`, `.topics` | the typed service manifest |
|
|
301
|
+
| `hit.serves(type_name)` | does this service offer a topic of that type? for picking one out of a search result |
|
|
230
302
|
| `hit.topic(type_name)` | a `Topic`, URL taken from the manifest, never built |
|
|
231
303
|
| `hit.reaches(lat, lon, radius_m)` | does the declared coverage reach a point |
|
|
232
304
|
| `topic.type`, `.url`, `.name`, `.durability`, `.access`, `.fqn` | what the manifest said |
|
|
@@ -236,6 +308,9 @@ call goes away and the bar is one line.
|
|
|
236
308
|
| `resolved.doc`, `.uri`, `.etag`, `.assets` | the resolved manifest |
|
|
237
309
|
| `resolved.asset_url(i)`, `.fetch_asset(i)` | the URL it would fetch, and the verified bytes |
|
|
238
310
|
| `written.doc`, `.etag`, `.created`, `.manifest` | what a publish returned |
|
|
311
|
+
| `sd.service_manifest(service_id, kind, authority=, scene=, profiles=, request=, response=, limits=, auth=, coverage=, cells=)` | the contract behind an announce; every member a claim, so every member an argument |
|
|
312
|
+
| `sd.capabilities(supported=, preferred=, features=)`, `sd.profile(name, minor=)` | what a service supports, for `service_announce(caps=)` |
|
|
313
|
+
| `sd.asset(path_or_bytes, media_type=)` / `sd.asset_ref(url, hash=, media_type=)` | an asset hashed from its bytes, or a reference to one hosted elsewhere |
|
|
239
314
|
| `sd.records` | the builders, listed above |
|
|
240
315
|
| `sd.lift(fqn, doc)` | the lift on its own, for JSON you already have |
|
|
241
316
|
| `sd.SPEC_COMMIT`, `sd.APPENDIX_N`, `sd.__version__` | what this speaks |
|
|
@@ -260,7 +335,7 @@ One family, built from the endpoint's own N.8 problem documents — `status`,
|
|
|
260
335
|
`type`, `detail` carried, not re-invented. Everything below derives from
|
|
261
336
|
`SpatialDDSError`, so one `except` catches the lot.
|
|
262
337
|
|
|
263
|
-
| | |
|
|
338
|
+
| exception | raised when |
|
|
264
339
|
| --- | --- |
|
|
265
340
|
| `Unreachable` | the endpoint did not answer; names the URL and the serve line |
|
|
266
341
|
| `Unauthorized` | 401; says to pass `token=` and where the token is printed |
|
|
@@ -281,12 +356,24 @@ Latched reads revalidate with the ETag the endpoint gave; `fetch_asset` checks
|
|
|
281
356
|
the bytes. Opting out exists and is loud: `verify_integrity=False`, at the call
|
|
282
357
|
site, nowhere else.
|
|
283
358
|
|
|
284
|
-
## Not in 0.
|
|
359
|
+
## Not in 0.3.1
|
|
285
360
|
|
|
286
361
|
Stated so nobody waits for them: no async API, no caching beyond ETag
|
|
287
362
|
revalidation, no retry policy, no MCAP tooling beyond the journal the server
|
|
288
363
|
writes, and no DDS dependency of any kind. The wire is HTTP and a WebSocket.
|
|
289
364
|
|
|
365
|
+
0.3.1 is this page: two code blocks on it had no gate behind them and both
|
|
366
|
+
were broken, and a long description is baked into the artifact, so the page on
|
|
367
|
+
PyPI only heals with a release. Every block here is executed by a gate now,
|
|
368
|
+
and the gate also refuses a relative link, an empty table header and a serve
|
|
369
|
+
line naming a flag that does not exist.
|
|
370
|
+
|
|
371
|
+
`sd.asset` changed meaning in 0.3.0, which is why that version moved: it now
|
|
372
|
+
reads bytes and computes a hash, where in 0.2.0 it took a URL and a hash you
|
|
373
|
+
supplied. That second form is `sd.asset_ref`. A published release has to stay
|
|
374
|
+
reproducible from a public commit, so the API could not change under the same
|
|
375
|
+
number.
|
|
376
|
+
|
|
290
377
|
On the registry, four things are deferred rather than missing, and the same
|
|
291
378
|
four are on the 1.9 candidates list: **leases** (nothing expires; `ttl_sec` is
|
|
292
379
|
advice to caches), **per-provider scopes** (one token, one operator),
|
|
@@ -19,6 +19,10 @@ not have to assemble member by member.
|
|
|
19
19
|
specification versions move independently; `spatialdds.SPEC_COMMIT` is the one
|
|
20
20
|
that says what the wire looks like.
|
|
21
21
|
|
|
22
|
+
The source, the reference gateway, the recordings and every gate behind the
|
|
23
|
+
claims on this page are at [https://github.com/OpenArCloud/spatialdds-web](https://github.com/OpenArCloud/spatialdds-web). Every code block below is executed
|
|
24
|
+
by one of those gates on every run, against an endpoint started for it.
|
|
25
|
+
|
|
22
26
|
## Your own registry, in one command
|
|
23
27
|
|
|
24
28
|
```sh
|
|
@@ -48,34 +52,101 @@ Appendix N's schemas make every member of a record required, so an `Announce`
|
|
|
48
52
|
is eighteen members, a `ContentAnnounce` sixteen, a `GeoAnchor` nine. The
|
|
49
53
|
builders take the arguments you actually have:
|
|
50
54
|
|
|
55
|
+
<!-- bar: verbs -->
|
|
51
56
|
```python
|
|
52
57
|
import spatialdds as sd
|
|
58
|
+
ep = sd.connect("http://127.0.0.1:8787", token="not-a-secret", scene="mytown")
|
|
59
|
+
a = ep.publish_service(service_id="mytown-vps", kind="VPS",
|
|
60
|
+
coverage=sd.circle(30.2849, -97.7393, 500))
|
|
61
|
+
b = ep.publish_content(
|
|
62
|
+
content_id="duck", title="A duck", position=(30.2849, -97.7393),
|
|
63
|
+
asset=sd.asset_ref("https://cdn.example/duck.gltf",
|
|
64
|
+
media_type="model/gltf+json"))
|
|
65
|
+
c = ep.publish_anchor("anchor/duck-perch", lat=30.2849, lon=-97.7393,
|
|
66
|
+
method="VisualFix", sigma_enu_m=(0.25, 0.25, 0.5),
|
|
67
|
+
content="duck")
|
|
68
|
+
print("service", a.created, "| content", b.created, "| anchor", c.created)
|
|
69
|
+
print("manifest", b.manifest.doc["id"])
|
|
70
|
+
```
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ep.publish_content(content_id="duck", title="Duck", position=(30.2849, -97.7393),
|
|
58
|
-
asset="https://cdn.example/duck.gltf", sha256=digest)
|
|
59
|
-
ep.publish_anchor("anchor/duck-perch", lat=30.2849, lon=-97.7393,
|
|
60
|
-
method="VisualFix", sigma_enu_m=(0.25, 0.25, 0.5),
|
|
61
|
-
content="duck")
|
|
72
|
+
```console
|
|
73
|
+
service True | content True | anchor True
|
|
74
|
+
manifest spatialdds://demo.spatialdds.org/mytown/content/duck
|
|
62
75
|
```
|
|
63
76
|
|
|
64
77
|
`publish_service` and `publish_content` are two PUTs each -- the manifest
|
|
65
78
|
first, then the record that points at it -- and return what `publish` returns,
|
|
66
79
|
with the manifest's own result on `.manifest`.
|
|
67
80
|
|
|
81
|
+
A full service contract is the same one call, with the claims stated:
|
|
82
|
+
|
|
83
|
+
<!-- bar: contract -->
|
|
84
|
+
```python
|
|
85
|
+
import spatialdds as sd
|
|
86
|
+
ep = sd.connect("http://127.0.0.1:8787", token="not-a-secret", scene="mytown")
|
|
87
|
+
area = sd.circle(30.2849, -97.7393, 500)
|
|
88
|
+
w = ep.publish_service(
|
|
89
|
+
service_id="mytown-vps", kind="VPS", coverage=area,
|
|
90
|
+
caps=sd.capabilities(
|
|
91
|
+
supported=[sd.profile("spatial.core", minor=8)],
|
|
92
|
+
features=["web-binding/json"]),
|
|
93
|
+
manifest=sd.service_manifest(
|
|
94
|
+
"mytown-vps", "VPS", authority=ep.authority, scene="mytown",
|
|
95
|
+
profiles=["Core", "AR+Geo"],
|
|
96
|
+
request={"features_topic": "feat.keyframe"},
|
|
97
|
+
response={"minimal": "geo.fix"},
|
|
98
|
+
limits={"max_fps": 10},
|
|
99
|
+
auth={"scheme": "bearer",
|
|
100
|
+
"issuer": "https://registry.mytown.example"},
|
|
101
|
+
coverage=area, cells=sd.cells(area)))
|
|
102
|
+
print("profiles", w.manifest.doc["profiles"])
|
|
103
|
+
print("limits ", w.manifest.doc["limits"])
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```console
|
|
107
|
+
profiles ['Core', 'AR+Geo']
|
|
108
|
+
limits {'max_fps': 10}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Every one of those is something a service operator knows and a library cannot
|
|
112
|
+
guess. [The mytown walkthrough](https://github.com/OpenArCloud/spatialdds-web/blob/main/docs/MYTOWN.md) publishes that
|
|
113
|
+
and prints the whole served document;
|
|
114
|
+
[`gates/builders.py`](https://github.com/OpenArCloud/spatialdds-web/blob/main/gates/builders.py) rebuilds that
|
|
115
|
+
repository's hand-authored `austin-vps` and duck records through these calls
|
|
116
|
+
and compares them member by member, which is what makes "every member is
|
|
117
|
+
reachable from an argument" a check rather than a claim.
|
|
118
|
+
|
|
119
|
+
**Two forms for an asset, because there are two situations.**
|
|
120
|
+
`sd.asset(path_or_bytes, media_type=...)` reads the data and computes the hash
|
|
121
|
+
and the byte count itself: a hash nobody typed is a hash a reader can trust.
|
|
122
|
+
`sd.asset_ref(url, hash=..., media_type=...)` points at something somebody
|
|
123
|
+
else hosts, and the hash comes from whoever has the bytes. Pass no hash and
|
|
124
|
+
the reference carries no integrity claim, which costs a reader one thing:
|
|
125
|
+
`fetch_asset()` raises rather than reporting a check it could not perform
|
|
126
|
+
([finding 31](https://github.com/OpenArCloud/spatialdds-web/blob/main/WEB-BINDING-FINDINGS.md)).
|
|
127
|
+
`publish_content` takes either, or a path, or bytes, or a URL.
|
|
128
|
+
|
|
68
129
|
The builders are also usable on their own: `sd.service_announce`,
|
|
69
|
-
`sd.
|
|
70
|
-
`sd.
|
|
130
|
+
`sd.service_manifest`, `sd.content_announce`, `sd.content_manifest`,
|
|
131
|
+
`sd.geo_anchor`, with `sd.circle`, `sd.bbox`, `sd.geopose`, `sd.cov_pos3`,
|
|
132
|
+
`sd.cov_none`, `sd.profile`, `sd.capabilities`, `sd.asset_ref`,
|
|
71
133
|
`sd.frame`,
|
|
72
134
|
`sd.earth_fixed`, `sd.stamp`, `sd.topic`, `sd.asset`, `sd.cells`,
|
|
73
135
|
`sd.service_uri` and `sd.content_uri`. Each returns a plain dict in canonical
|
|
74
136
|
form, publishable as it stands and dumpable as the authoring JSON this
|
|
75
137
|
repository's tools read. `spatialdds.records` is the module they live in.
|
|
76
138
|
|
|
77
|
-
**
|
|
78
|
-
|
|
139
|
+
**Conventions fill plumbing. They never fill claims.** A stamp of now, an
|
|
140
|
+
earth-fixed frame, a checksum computed from the record itself: plumbing, with
|
|
141
|
+
one sensible value each, and filling them saves you writing out what you had
|
|
142
|
+
no choice about. A profile list, a rate limit, a request topic, an auth
|
|
143
|
+
scheme, a title: claims, and no default for any of them is honest. State
|
|
144
|
+
nothing and you publish a minimal record, which is *correct*; state a
|
|
145
|
+
contract and you publish it in one call. There is nothing in between for a
|
|
146
|
+
library to invent.
|
|
147
|
+
|
|
148
|
+
**What they fill in, and what they refuse to.** The conventions above are
|
|
149
|
+
documented where they are applied: a stamp of now unless you pass `when=`, an
|
|
79
150
|
earth-fixed frame unless you pass one, and URIs assembled under a stated
|
|
80
151
|
authority -- never a guessed one. `ep.publish_*` gets the authority from the
|
|
81
152
|
endpoint itself (`ep.authority`, which asks the resolver metadata, then the
|
|
@@ -136,7 +207,7 @@ the latched set is complete.
|
|
|
136
207
|
```python
|
|
137
208
|
import spatialdds as sd
|
|
138
209
|
ep = sd.connect("http://127.0.0.1:8787")
|
|
139
|
-
svc = ep.search(geohash="9v6krc")
|
|
210
|
+
svc = next(h for h in ep.search(geohash="9v6krc") if h.serves("geo_anchor"))
|
|
140
211
|
for ev in svc.topic("geo_anchor").subscribe(timeout=10):
|
|
141
212
|
if isinstance(ev, sd.EndOfReplay): print("latched set complete:", ev.replayed); break
|
|
142
213
|
print(" ", ev.key, ev.data["method"], ev.data["geopose"]["stamp"]["sec"])
|
|
@@ -189,7 +260,7 @@ call goes away and the bar is one line.
|
|
|
189
260
|
|
|
190
261
|
## The surface
|
|
191
262
|
|
|
192
|
-
| | |
|
|
263
|
+
| call | what it does |
|
|
193
264
|
| --- | --- |
|
|
194
265
|
| `sd.connect(base, token=None, verify_integrity=True, timeout=15.0, lifted=True, scene="default", authority=None)` | an `Endpoint`; nothing is fetched until you ask |
|
|
195
266
|
| `ep.search(geohash=None, kinds=None)` | §3.3.0 search → a list of `Hit` |
|
|
@@ -203,6 +274,7 @@ call goes away and the bar is one line.
|
|
|
203
274
|
| `ep.publish_anchor(anchor_id, lat=, lon=, method=, content=None, ...)` | build and PUT an anchor, optionally naming content |
|
|
204
275
|
| `ep.withdraw(topic=, key=)` | DELETE: leaves the latched set and disposes |
|
|
205
276
|
| `hit.service_id`, `.kind`, `.coverage`, `.topics` | the typed service manifest |
|
|
277
|
+
| `hit.serves(type_name)` | does this service offer a topic of that type? for picking one out of a search result |
|
|
206
278
|
| `hit.topic(type_name)` | a `Topic`, URL taken from the manifest, never built |
|
|
207
279
|
| `hit.reaches(lat, lon, radius_m)` | does the declared coverage reach a point |
|
|
208
280
|
| `topic.type`, `.url`, `.name`, `.durability`, `.access`, `.fqn` | what the manifest said |
|
|
@@ -212,6 +284,9 @@ call goes away and the bar is one line.
|
|
|
212
284
|
| `resolved.doc`, `.uri`, `.etag`, `.assets` | the resolved manifest |
|
|
213
285
|
| `resolved.asset_url(i)`, `.fetch_asset(i)` | the URL it would fetch, and the verified bytes |
|
|
214
286
|
| `written.doc`, `.etag`, `.created`, `.manifest` | what a publish returned |
|
|
287
|
+
| `sd.service_manifest(service_id, kind, authority=, scene=, profiles=, request=, response=, limits=, auth=, coverage=, cells=)` | the contract behind an announce; every member a claim, so every member an argument |
|
|
288
|
+
| `sd.capabilities(supported=, preferred=, features=)`, `sd.profile(name, minor=)` | what a service supports, for `service_announce(caps=)` |
|
|
289
|
+
| `sd.asset(path_or_bytes, media_type=)` / `sd.asset_ref(url, hash=, media_type=)` | an asset hashed from its bytes, or a reference to one hosted elsewhere |
|
|
215
290
|
| `sd.records` | the builders, listed above |
|
|
216
291
|
| `sd.lift(fqn, doc)` | the lift on its own, for JSON you already have |
|
|
217
292
|
| `sd.SPEC_COMMIT`, `sd.APPENDIX_N`, `sd.__version__` | what this speaks |
|
|
@@ -236,7 +311,7 @@ One family, built from the endpoint's own N.8 problem documents — `status`,
|
|
|
236
311
|
`type`, `detail` carried, not re-invented. Everything below derives from
|
|
237
312
|
`SpatialDDSError`, so one `except` catches the lot.
|
|
238
313
|
|
|
239
|
-
| | |
|
|
314
|
+
| exception | raised when |
|
|
240
315
|
| --- | --- |
|
|
241
316
|
| `Unreachable` | the endpoint did not answer; names the URL and the serve line |
|
|
242
317
|
| `Unauthorized` | 401; says to pass `token=` and where the token is printed |
|
|
@@ -257,12 +332,24 @@ Latched reads revalidate with the ETag the endpoint gave; `fetch_asset` checks
|
|
|
257
332
|
the bytes. Opting out exists and is loud: `verify_integrity=False`, at the call
|
|
258
333
|
site, nowhere else.
|
|
259
334
|
|
|
260
|
-
## Not in 0.
|
|
335
|
+
## Not in 0.3.1
|
|
261
336
|
|
|
262
337
|
Stated so nobody waits for them: no async API, no caching beyond ETag
|
|
263
338
|
revalidation, no retry policy, no MCAP tooling beyond the journal the server
|
|
264
339
|
writes, and no DDS dependency of any kind. The wire is HTTP and a WebSocket.
|
|
265
340
|
|
|
341
|
+
0.3.1 is this page: two code blocks on it had no gate behind them and both
|
|
342
|
+
were broken, and a long description is baked into the artifact, so the page on
|
|
343
|
+
PyPI only heals with a release. Every block here is executed by a gate now,
|
|
344
|
+
and the gate also refuses a relative link, an empty table header and a serve
|
|
345
|
+
line naming a flag that does not exist.
|
|
346
|
+
|
|
347
|
+
`sd.asset` changed meaning in 0.3.0, which is why that version moved: it now
|
|
348
|
+
reads bytes and computes a hash, where in 0.2.0 it took a URL and a hash you
|
|
349
|
+
supplied. That second form is `sd.asset_ref`. A published release has to stay
|
|
350
|
+
reproducible from a public commit, so the API could not change under the same
|
|
351
|
+
number.
|
|
352
|
+
|
|
266
353
|
On the registry, four things are deferred rather than missing, and the same
|
|
267
354
|
four are on the 1.9 candidates list: **leases** (nothing expires; `ttl_sec` is
|
|
268
355
|
advice to caches), **per-provider scopes** (one token, one operator),
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spatialdds"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
description = "SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -8,7 +8,8 @@ can act on.
|
|
|
8
8
|
import spatialdds as sd
|
|
9
9
|
|
|
10
10
|
ep = sd.connect("https://demo.spatialdds.org")
|
|
11
|
-
svc = ep.search(geohash="9v6krc")
|
|
11
|
+
svc = next(h for h in ep.search(geohash="9v6krc")
|
|
12
|
+
if h.serves("geo_anchor"))
|
|
12
13
|
for a in svc.topic("geo_anchor").latched():
|
|
13
14
|
print(a["anchor_id"], a["geopose"]["stamp"]["sec"]) # an int, not "17…"
|
|
14
15
|
|
|
@@ -30,12 +31,13 @@ __all__ = [
|
|
|
30
31
|
# The record builders, re-exported so that `sd.circle(...)` reads the way
|
|
31
32
|
# it does in the documentation. They live in `spatialdds.records`.
|
|
32
33
|
"records", "circle", "bbox", "earth_fixed", "frame", "stamp", "geopose",
|
|
33
|
-
"cov_pos3", "cov_none", "topic", "asset", "cells",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"cov_pos3", "cov_none", "topic", "asset", "asset_ref", "cells",
|
|
35
|
+
"profile", "capabilities",
|
|
36
|
+
"service_announce", "service_manifest", "content_announce",
|
|
37
|
+
"content_manifest", "geo_anchor", "service_uri", "content_uri",
|
|
36
38
|
]
|
|
37
39
|
|
|
38
|
-
__version__ = "0.
|
|
40
|
+
__version__ = "0.3.1"
|
|
39
41
|
#: The SpatialDDS-spec commit whose Appendix N and generated artifacts this
|
|
40
42
|
#: package implements. Package versions and specification versions move
|
|
41
43
|
#: independently; this is the one that says what the wire looks like.
|
|
@@ -329,9 +329,10 @@ class Endpoint:
|
|
|
329
329
|
def publish_content(self, content_id: str, *,
|
|
330
330
|
position: Optional[Any] = None,
|
|
331
331
|
coverage: Any = None, radius_m: float = 10.0,
|
|
332
|
-
asset:
|
|
332
|
+
asset: Any = None,
|
|
333
333
|
media_type: str = "application/octet-stream",
|
|
334
|
-
sha256: str = "",
|
|
334
|
+
sha256: str = "", asset_uri: str = "",
|
|
335
|
+
assets: Any = None,
|
|
335
336
|
scene: Optional[str] = None,
|
|
336
337
|
stream: str = "content",
|
|
337
338
|
title: str = "", summary: str = "",
|
|
@@ -361,7 +362,8 @@ class Endpoint:
|
|
|
361
362
|
"position=(lat, lon) or coverage=sd.circle(...).")
|
|
362
363
|
items = list(assets) if assets else []
|
|
363
364
|
if asset is not None:
|
|
364
|
-
items.append(
|
|
365
|
+
items.append(self._asset_entry(asset, media_type, sha256,
|
|
366
|
+
asset_uri))
|
|
365
367
|
man = records.content_manifest(
|
|
366
368
|
content_id, authority=self.authority, scene=sc, assets=items,
|
|
367
369
|
title=title, summary=summary, tags=tags, class_id=class_id,
|
|
@@ -377,6 +379,30 @@ class Endpoint:
|
|
|
377
379
|
w.manifest = mw
|
|
378
380
|
return w
|
|
379
381
|
|
|
382
|
+
def _asset_entry(self, asset: Any, media_type: str, sha256: str,
|
|
383
|
+
asset_uri: str) -> Dict[str, Any]:
|
|
384
|
+
"""Either asset form, from whatever the caller actually had.
|
|
385
|
+
|
|
386
|
+
A dict is used as it stands -- `sd.asset()` and `sd.asset_ref()`
|
|
387
|
+
return those. A URL becomes a reference, with whatever hash was
|
|
388
|
+
supplied. Bytes, or a path to a file that exists, are hashed from the
|
|
389
|
+
data: `sd.asset()` does that, and the URI defaults to this endpoint's
|
|
390
|
+
own asset route under the authority it advertises, which is where the
|
|
391
|
+
bytes will be if you put them there. Pass `asset_uri=` for anywhere
|
|
392
|
+
else.
|
|
393
|
+
"""
|
|
394
|
+
if isinstance(asset, dict):
|
|
395
|
+
return asset
|
|
396
|
+
if isinstance(asset, str) and asset.startswith(("http://", "https://")):
|
|
397
|
+
return records.asset_ref(asset, media_type=media_type,
|
|
398
|
+
hash=sha256)
|
|
399
|
+
name = ""
|
|
400
|
+
if not isinstance(asset, (bytes, bytearray)):
|
|
401
|
+
name = str(asset).rsplit("/", 1)[-1]
|
|
402
|
+
uri = asset_uri or (f"https://{self.authority}/assets/{name}"
|
|
403
|
+
if name else "")
|
|
404
|
+
return records.asset(asset, media_type=media_type, uri=uri)
|
|
405
|
+
|
|
380
406
|
def publish_anchor(self, anchor_id: str, *, lat: float, lon: float,
|
|
381
407
|
method: str, alt_m: float = 0.0,
|
|
382
408
|
content: Optional[str] = None,
|
|
@@ -444,6 +470,18 @@ class Hit:
|
|
|
444
470
|
f"{self.service_id or 'this service'} serves no {type_name!r} "
|
|
445
471
|
f"topic; it offers: {have}")
|
|
446
472
|
|
|
473
|
+
def serves(self, type_name: str) -> bool:
|
|
474
|
+
"""Does this service offer a topic of that type?
|
|
475
|
+
|
|
476
|
+
For picking a service out of a search result, which is what `search`
|
|
477
|
+
returns and what nobody should be indexing by position: the order is
|
|
478
|
+
the endpoint's, and it changes the moment somebody publishes another
|
|
479
|
+
service into the same cell. `search(...)[0]` on the front page read
|
|
480
|
+
the fountain's anchors until the page's own publish block ran, and
|
|
481
|
+
then read a VPS that serves none.
|
|
482
|
+
"""
|
|
483
|
+
return any(t.get("type") == type_name for t in self.topics)
|
|
484
|
+
|
|
447
485
|
def reaches(self, lat: float, lon: float, radius_m: float) -> bool:
|
|
448
486
|
"""Does the declared coverage come within `radius_m` of the point?"""
|
|
449
487
|
return _coverage_reaches(self.coverage, lat, lon, radius_m)
|