Filtering by a location
Narrow down your query results to only include datapoints within a specific geographic area of interest by providing a geometry for the target region.
When querying, you can specify arbitrary geometries as an area of interest. Tilebox currently supports Polygon and MultiPolygon geometries as query filters.
Filtering by Area of Interest
To filter by an area of interest, use a Polygon or MultiPolygon geometry as the spatial extent parameter.
Here is how to query Sentinel-2 L2A data over Colorado for a specific day in April 2025.
from shapely import Polygon
from tilebox.datasets import Client
area = Polygon( # area roughly covering the state of Colorado
((-109.05, 41.00), (-109.045, 37.0), (-102.05, 37.0), (-102.05, 41.00), (-109.05, 41.00)),
)
client = Client()
sentinel2_msi = client.dataset("open_data.copernicus.sentinel2_msi")
data = sentinel2_msi.query(
collections=["S2A_S2MSI2A", "S2B_S2MSI2A", "S2C_S2MSI2A"],
temporal_extent=("2025-04-02", "2025-04-03"),
spatial_extent=area,
)startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 4, 3, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // area roughly covering the state of Colorado
{{-109.05, 41.00}, {-109.045, 37.0}, {-102.05, 37.0}, {-102.05, 41.00}, {-109.05, 41.00}},
}
ctx := context.Background()
client := datasets.NewClient()
dataset, err := client.Datasets.Get(ctx, "open_data.copernicus.sentinel2_msi")
if err != nil {
log.Fatalf("Failed to get dataset: %v", err)
}
collection, err := client.Collections.Get(ctx, dataset.ID, "S2A_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
collectionB, err := client.Collections.Get(ctx, dataset.ID, "S2B_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
collectionC, err := client.Collections.Get(ctx, dataset.ID, "S2C_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
var datapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID, collectionB.ID, collectionC.ID),
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtent(area),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}Spatial filter modes
The spatial filter mode controls the relationship between the filter geometry and each returned datapoint geometry. Containment mode names identify which geometry contains the other.
| Mode | Relationship |
|---|---|
intersects |
The datapoint geometry intersects the filter geometry |
filter_contains_geometry |
The filter geometry contains the datapoint geometry |
geometry_contains_filter |
The datapoint geometry contains the filter geometry |

Intersects
The intersects mode is the default for spatial queries. It matches all datapoints whose geometries intersect with the filter geometry.
area = Polygon( # area roughly covering the state of Colorado
((-109.05, 41.00), (-109.045, 37.0), (-102.05, 37.0), (-102.05, 41.00), (-109.05, 41.00)),
)
data = dataset.query(
temporal_extent=("2025-04-02", "2025-04-03"),
# intersects is the default, so can also be omitted entirely
spatial_extent={"geometry": area, "mode": "intersects"},
)
print(f"There are {data.sizes['time']} Sentinel-2A granules intersecting the area of Colorado on April 2nd, 2025")startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 4, 3, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // area roughly covering the state of Colorado
{{-109.05, 41.00}, {-109.045, 37.0}, {-102.05, 37.0}, {-102.05, 41.00}, {-109.05, 41.00}},
}
var datapoints []*examplesv1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtentFilter(&query.SpatialFilter{
Geometry: area,
// intersects is the default, so can also be omitted entirely
Mode: datasetsv1.SpatialFilterMode_SPATIAL_FILTER_MODE_INTERSECTS,
}),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}There are 27 Sentinel-2A granules intersecting the area of Colorado on April 2nd, 2025Filter contains geometry
The filter_contains_geometry mode matches datapoints whose geometries are fully contained within the filter geometry.
area = Polygon( # area roughly covering the state of Colorado
((-109.05, 41.00), (-109.045, 37.0), (-102.05, 37.0), (-102.05, 41.00), (-109.05, 41.00)),
)
data = collection.query(
temporal_extent=("2025-04-01", "2025-05-02"),
spatial_extent={"geometry": area, "mode": "filter_contains_geometry"},
)
print(f"There are {data.sizes['time']} Sentinel-2A granules fully contained within the area of Colorado on April 2nd, 2025")startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 5, 2, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // area roughly covering the state of Colorado
{{-109.05, 41.00}, {-109.045, 37.0}, {-102.05, 37.0}, {-102.05, 41.00}, {-109.05, 41.00}},
}
var datapoints []*examplesv1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID),
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtentFilter(&query.SpatialFilter{
Geometry: area,
Mode: datasetsv1.SpatialFilterMode_SPATIAL_FILTER_MODE_FILTER_CONTAINS_GEOMETRY,
}),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}There are 16 Sentinel-2A granules fully contained within the area of Colorado on April 2nd, 2025Geometry contains filter
The geometry_contains_filter mode matches datapoints whose geometries fully contain the filter geometry. For example, a small filter geometry within Colorado can match a datapoint whose geometry covers the entire state.
area = Polygon( # a small area around Denver
((-105.1, 39.8), (-105.1, 39.6), (-104.8, 39.6), (-104.8, 39.8), (-105.1, 39.8)),
)
data = collection.query(
temporal_extent=("2025-04-01", "2025-05-02"),
spatial_extent={"geometry": area, "mode": "geometry_contains_filter"},
)startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 5, 2, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // a small area around Denver
{{-105.1, 39.8}, {-105.1, 39.6}, {-104.8, 39.6}, {-104.8, 39.8}, {-105.1, 39.8}},
}
var datapoints []*examplesv1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID),
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtentFilter(&query.SpatialFilter{
Geometry: area,
Mode: datasetsv1.SpatialFilterMode_SPATIAL_FILTER_MODE_GEOMETRY_CONTAINS_FILTER,
}),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}Antimeridian Crossings
In many applications, geometries that cross the antimeridian cause issues. Since such geometries are common in satellite data, Tilebox does take extra care to handle them out of the box correctly, by building the necessary internal spatial index structures in a way that correctly handles antimeridian crossings and pole coverings.
To get accurate results also at query time, it’s recommend to use the spherical coordinate reference system
for querying (which is the default), as it correctly handles the non-linearity introduced by the antimeridian in cartesian space.
Coordinate reference system
Geometry intersection and containment checks can either be performed in a 3D spherical coordinate system
or in a standard 2D cartesian lat/lon coordinate system.


Spherical
The spherical coordinate reference system is the default and recommended choice. It correctly handles antimeridian
crossings and is the most robust option, regardless of how datapoint geometries are cut along the antimeridian.
When querying with the spherical coordinate reference system, Tilebox automatically converts all geometries to
their x, y, z coordinates on the unit sphere and performs the intersection and containment checks in 3D.
area = Polygon( # area roughly covering the state of Colorado
((-109.05, 41.00), (-109.045, 37.0), (-102.05, 37.0), (-102.05, 41.00), (-109.05, 41.00)),
)
data = dataset.query(
temporal_extent=("2025-04-01", "2025-05-02"),
# spherical is the default, so can also be omitted entirely
spatial_extent={"geometry": area, "coordinate_system": "spherical"},
)startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 5, 2, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // area roughly covering the state of Colorado
{{-109.05, 41.00}, {-109.045, 37.0}, {-102.05, 37.0}, {-102.05, 41.00}, {-109.05, 41.00}},
}
var datapoints []*examplesv1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtentFilter(&query.SpatialFilter{
Geometry: area,
// spherical is the default, so can also be omitted entirely
CoordinateSystem: datasetsv1.SpatialCoordinateSystem_SPATIAL_COORDINATE_SYSTEM_SPHERICAL,
}),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}Cartesian
Tilebox can also be configured to use a standard 2D cartesian lat/lon coordinate system for geometry intersection and containment checks.
This is done by specifying the cartesian coordinate reference system when querying.
area = Polygon( # area roughly covering the state of Colorado
((-109.05, 41.00), (-109.045, 37.0), (-102.05, 37.0), (-102.05, 41.00), (-109.05, 41.00)),
)
data = dataset.query(
temporal_extent=("2025-04-01", "2025-05-02"),
spatial_extent={"geometry": area, "coordinate_system": "cartesian"},
)startDate := time.Date(2025, 4, 2, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, 5, 2, 0, 0, 0, 0, time.UTC)
area := orb.Polygon{ // area roughly covering the state of Colorado
{{-109.05, 41.00}, {-109.045, 37.0}, {-102.05, 37.0}, {-102.05, 41.00}, {-109.05, 41.00}},
}
var datapoints []*examplesv1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
datasets.WithSpatialExtentFilter(&query.SpatialFilter{
Geometry: area,
CoordinateSystem: datasetsv1.SpatialCoordinateSystem_SPATIAL_COORDINATE_SYSTEM_CARTESIAN,
}),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}