Features & Annotations¶
Vector data associated with overhead imagery — bounding boxes, polygons,
points, and other geometries — can exist in two coordinate spaces.
Image annotations live in pixel coordinates relative to the image
grid: an ML model outputs a bounding box at pixel (512, 384), or an
analyst draws a polygon around a building in row/column space.
Geospatial features live in geographic coordinates (longitude,
latitude, elevation) and can be plotted on a map, stored in a GIS
database, or fused across multiple collects.
This package provides utilities for converting between the two:
Geolocatorconverts image annotations into geospatial features — assigning WGS-84 coordinates to pixel-space detections using the image’s sensor model.Projectorconverts geospatial features into image annotations — computing pixel coordinates for known geographic locations so they can be rendered as overlays on the imagery.STRFeature2DSpatialIndexprovides efficient region queries over features in either coordinate space.
Both transforms delegate the actual coordinate math to a sensor model. This chapter focuses on the higher-level workflow: encoding detections, transforming batches of features, and querying the results spatially.
All input and output uses standard GeoJSON (RFC 7946), extended with image coordinate properties described in the encoding convention at the end of this chapter.
Geolocation (Image → World)¶
The Geolocator reads imageGeometry and imageBBox from each
feature, transforms the pixel values through the sensor model, and
populates the standard GeoJSON "geometry" and "bbox" members with
WGS-84 coordinates. It also adds center_longitude and
center_latitude properties (in degrees).
import geojson
from aws.osml.io import IO
from aws.osml.metadata import load_sensor_model
from aws.osml.features import Geolocator, ImagedFeaturePropertyAccessor
detections = [
geojson.Feature(properties={
"imageGeometry": {"type": "Point", "coordinates": [512, 384]},
"imageBBox": [480, 352, 544, 416],
}),
]
with IO.open("image.ntf", "r") as reader:
sensor_model = load_sensor_model(reader)
geolocator = Geolocator(
property_accessor=ImagedFeaturePropertyAccessor(),
sensor_model=sensor_model,
)
geolocator.geolocate_features(detections)
# Each feature now has standard GeoJSON geometry with WGS-84 coordinates
print(detections[0]["geometry"])
# {"type": "Point", "coordinates": [-77.0365, 38.8977, 0.0]}
By default, the Geolocator skips features that already have a
non-None geometry — avoiding overwrites from a previous geolocation
pass or an external source. Pass force=True to re-geolocate all
features regardless.
For batch geolocation the Geolocator builds a regular grid of
sensor-model evaluations across the feature extent and uses bivariate
spline interpolation for individual coordinates. This provides sub-pixel
accuracy at a fraction of the cost of per-point evaluation. Increase
approximation_grid_size (default 11) for large extents where higher
interpolation fidelity is needed.
Without an elevation model, the geolocator assumes all detections are at sea level — introducing lateral error proportional to terrain height and sensor look angle. Supply an elevation model for improved accuracy:
from aws.osml.photogrammetry import ConstantElevationModel
geolocator = Geolocator(
property_accessor=ImagedFeaturePropertyAccessor(),
sensor_model=sensor_model,
elevation_model=ConstantElevationModel(500.0),
)
See also
Elevation Models for DTED-based and other terrain sources. Sensor Models for details on the image-to-world coordinate transform.
Projection (World → Image)¶
The Projector is the inverse operation. Given features with geographic
coordinates — from a GIS database, a reference layer, or a prior
geolocation pass — it computes where they appear in a specific image and
populates imageGeometry and imageBBox with pixel coordinates. Only
features whose projected geometry intersects the provided image bounds
are included in the result. Use cases include:
Rendering known annotations as overlays on a new collect
Pre-filtering a feature database to only features visible in a scene
Generating training labels by projecting ground truth into image space
import geojson
from aws.osml.io import IO
from aws.osml.metadata import load_sensor_model
from aws.osml.features import Projector, ImagedFeaturePropertyAccessor
features = [
geojson.Feature(
geometry=geojson.Point((-77.0365, 38.8977)),
properties={"label": "building-A"},
),
]
with IO.open("image.ntf", "r") as reader:
sensor_model = load_sensor_model(reader)
width, height = reader.segments[0].width, reader.segments[0].height
projector = Projector(
property_accessor=ImagedFeaturePropertyAccessor(),
sensor_model=sensor_model,
image_bounds=(0.0, 0.0, float(width), float(height)),
)
visible_features = projector.project_features(features)
for f in visible_features:
print(f["properties"]["imageGeometry"])
# {"type": "Point", "coordinates": [512.0, 384.0]}
Parameter |
Description |
|---|---|
|
Facade for reading/writing image coordinate properties. |
|
Sensor model used for |
|
Pixel-space bounding box |
|
Optional; queried for terrain height when a coordinate lacks an explicit Z value. |
|
If |
The Projector resolves vertex elevation by precedence: an explicit Z
coordinate in the GeoJSON is used first; if absent and an elevation
model is provided, it is queried; otherwise elevation defaults to 0.0 m.
Spatial Indexing¶
STRFeature2DSpatialIndex provides efficient spatial queries using
Shapely’s Sort-Tile-Recursive tree. By default it indexes features by
their imageGeometry, so query coordinates are in pixels:
import shapely
from aws.osml.features import STRFeature2DSpatialIndex
feature_collection = geojson.FeatureCollection(features)
index = STRFeature2DSpatialIndex(feature_collection)
query_box = shapely.box(400, 300, 600, 500)
results = index.find_intersects(query_box)
nearest = index.find_nearest(shapely.Point(500, 400), max_distance=100)
To index by geographic geometries instead (after geolocation), pass
use_image_geometries=False. The STR tree construction is O(n log n)
but subsequent queries are O(log n) — build the index once and reuse it
for multiple queries.
Encoding Image Coordinates in GeoJSON¶
GeoJSON does not define a way to represent image pixel coordinates. This library adopts a convention that stores image-space locations alongside standard geographic geometry, allowing a single feature to carry both representations as it moves through the geolocation or projection workflow.
Features that have not yet been assigned geographic coordinates use a
null "geometry" (section 3.2 of RFC 7946) and store image-space
locations in "properties":
imageGeometry — A GeoJSON-like Geometry Object where coordinate
values are in pixels. Origin (0, 0) is the top-left corner, x
increases right, y increases down. Coordinates are ordered (x, y).
imageBBox — An axis-aligned bounding box in pixels:
[min x, min y, max x, max y].
Supported Geometry Types¶
Type |
imageGeometry coordinates |
Geographic result after geolocation |
|---|---|---|
Point |
Single |
Single |
LineString |
List of |
List of |
Polygon |
Exterior + optional interior rings |
Geographic polygon |
Multi* / GeometryCollection |
Collection of the above |
Matching collection type |
Examples¶
Point — a single detected object with bounding box:
{
"type": "Feature",
"geometry": null,
"properties": {
"imageGeometry": {"type": "Point", "coordinates": [105.0, 5.0]},
"imageBBox": [100, 0, 110, 10]
}
}
LineString — a road segment traced through the image:
{
"type": "Feature",
"geometry": null,
"properties": {
"imageGeometry": {
"type": "LineString",
"coordinates": [[170.0, 45.0], [180.0, 47.0], [182.0, 49.0]]
}
}
}
Polygon — a building footprint (first coordinate repeated to close the ring):
{
"type": "Feature",
"geometry": null,
"properties": {
"imageGeometry": {
"type": "Polygon",
"coordinates": [[[0, 0], [10, 2], [10, 12], [0, 12], [0, 0]]]
},
"imageBBox": [0, 0, 10, 12]
}
}
Deprecated Property Formats¶
Earlier versions used different property names. The library still reads
these for backwards compatibility, but new code should use
imageGeometry and imageBBox exclusively.
Property |
Format |
Status |
|---|---|---|
|
GeoJSON geometry object |
Preferred |
|
|
Preferred |
|
Coordinate list |
Deprecated |
|
|
Deprecated |
|
GeoJSON-like |
Deprecated |