Skip to content
GuidesBlogPlaygroundDashboard

Geometry & Spatial Filters

Construct geometries and filter PlazaQL results by location using spatial predicates.

PlazaQL has four geometry constructors. All use lat, lng order (how humans say coordinates).

// Keyword args (self-documenting)
$home = point(lat: 38.9, lng: -77.0);
// Positional args (concise)
$work = point(38.85, -77.05);
$path = linestring(point(38.9, -77.0), point(38.88, -77.02), point(38.85, -77.05));

Polygons auto-close — you don’t need to repeat the first point:

$zone = polygon(
point(38.8, -77.1),
point(38.8, -76.9),
point(39.0, -76.9),
point(39.0, -77.1)
);

A shortcut for a rectangular polygon:

// Keyword: south, west, north, east
$area = bbox(south: 40.7, west: -74.0, north: 40.8, east: -73.9);
// Positional: s, w, n, e
$area = bbox(40.7, -74.0, 40.8, -73.9);

For named places, prefer boundary() over hardcoded coordinates:

$manhattan = boundary(name: "Manhattan");

Geometry constructors are composable — use variables to build complex shapes from simple ones:

$home = point(38.9, -77.0);
$work = point(38.85, -77.05);
$commute = linestring($home, $work);
search(amenity: "gas_station")
.around(distance: 200, geometry: $commute);

Spatial filters narrow results by location. They come after search() (or set operations) in the chain.

// Within 500m of a point
search(amenity: "cafe")
.around(distance: 500, geometry: point(48.85, 2.35));
// Within 200m of a route
$r = route(origin: point(40.748, -73.993), destination: point(40.755, -73.970), mode: "foot");
search(amenity: "bench")
.around(distance: 200, geometry: $r);
$paris = boundary(name: "Paris");
search(tourism: "museum").within(geometry: $paris);
// Inline polygon
search(building: *)
.within(geometry: polygon(
point(40.7, -74.0), point(40.7, -73.9),
point(40.8, -73.9), point(40.8, -74.0)
));
$river = search(way, waterway: "river", name: "Seine");
search(way, bridge: *).intersects(geometry: $river);
search(amenity: "pub")
.bbox(south: 51.50, west: -0.13, north: 51.53, east: -0.07);
search(amenity: "cafe")
.h3(cell: "8a2a1072b59ffff");

.contains() — feature fully contains geometry

Section titled “.contains() — feature fully contains geometry”
$park = point(40.7829, -73.9654);
search(way, leisure: "park").contains(geometry: $park);
$highway = search(way, highway: "motorway", ref: "I-95");
search(way, highway: "primary").crosses(geometry: $highway);

Every spatial filter has a negated form:

$flood_zone = polygon(...);
search(building: "residential")
.not_within(geometry: $flood_zone);
search(way, landuse: *)
.not_intersects(geometry: $protected_area);
search(way, building: *)
.not_contains(geometry: $exclusion_point);

Transforms modify the geometry or properties of results.

Add a buffer zone around each feature:

// 50-meter buffer around each school
search(amenity: "school")
.within(geometry: $city)
.buffer(50);

Turns points into circles, expands polygons outward. Useful for proximity analysis.

Simplify complex geometries using Douglas-Peucker tolerance:

// Simplify coastlines for overview display
search(way, natural: "coastline")
.bbox(40.0, -75.0, 42.0, -72.0)
.simplify(100);

The parameter is tolerance in meters — higher values produce simpler geometries.

Replace each feature’s geometry with its centroid:

// Get center points of all parks
search(way, leisure: "park")
.within(geometry: $city)
.centroid();

Converts any GeoSet to a PointSet.