geomermaids · Querying Parquetry

Recipes for the open datasets on parquetry.geomermaids.com: OpenStreetMap for North America, GMWID world infrastructures, FAO GAUL, Corine Land Cover and FEMA flood zones. Every file is a plain URL. No account, no key, no download step: DuckDB reads only the row groups a query needs. Most examples use the OpenStreetMap snapshots; the same patterns work on every dataset.

New to reading GeoParquet over HTTP? The Reading cookbook explains the setup, predicate pushdown and how to check what a query really downloads. To look before you query, GeoPQ Workbench has every Parquetry dataset built in.

1. One region, one theme, one URL

When you know the region and theme, name the file. DuckDB streams it over HTTPS with range requests.

Setup

Load the extensions once per session. The url() macro only keeps the examples short; a plain string URL works the same.

INSTALL httpfs; LOAD httpfs;
INSTALL spatial; LOAD spatial;

-- Coordinates are (longitude, latitude): tell the spatial extension, so
-- ST_Distance_Sphere and friends read the axes in that order.
SET geometry_always_xy = true;

CREATE OR REPLACE MACRO url(state, theme) AS
  'https://parquetry.geomermaids.com/osm/latest/country=' ||
  split_part(state, '-', 1) || '/state=' || state || '/' || theme || '.parquet';

-- The same thing without the macro:
SELECT count(*) FROM read_parquet(
  'https://parquetry.geomermaids.com/osm/latest/country=US/state=US-NY/buildings.parquet');

Buildings in a bounding box

A rectangle around Midtown Manhattan. The bbox.* clause is what makes it fast: its plain float statistics let any engine skip row groups before reading them. Each bbox is rounded outward to float32, so that clause alone returns a slight superset; st_intersects_extent makes the test exact. On engines without DuckDB's spatial functions (Spark, Trino, polars), keep the bbox.* clause and drop the last line.

SELECT count(*) AS buildings
FROM read_parquet(url('US-NY', 'buildings'))
WHERE bbox.xmin <= -73.98 AND bbox.xmax >= -73.99   -- prunes row groups
  AND bbox.ymin <= 40.76  AND bbox.ymax >= 40.75
  AND st_intersects_extent(geometry, ST_MakeEnvelope(-73.99, 40.75, -73.98, 40.76));

Tallest residential towers in a neighbourhood

Four columns out of twenty, and a spatial filter at the same time:

SELECT name, building, levels, height
FROM read_parquet(url('US-NY', 'buildings'))
WHERE building = 'residential'
  AND levels > 30
  AND bbox.xmin <= -73.95 AND bbox.xmax >= -73.99
  AND bbox.ymin <= 40.79  AND bbox.ymax >= 40.76
  AND st_intersects_extent(geometry, ST_MakeEnvelope(-73.99, 40.76, -73.95, 40.79))
ORDER BY levels DESC
LIMIT 10;

Restaurants and the buildings they sit in

A spatial join between two theme files. A bbox filter on each side keeps both reads to downtown Boston before ST_Contains runs:

SELECT b.name AS building, p.name AS restaurant, p.cuisine
FROM read_parquet(url('US-MA', 'buildings')) b
JOIN read_parquet(url('US-MA', 'pois')) p
  ON ST_Contains(b.geometry, p.geometry)
WHERE p.amenity = 'restaurant'
  AND b.name IS NOT NULL
  AND b.bbox.xmin <= -71.05 AND b.bbox.xmax >= -71.07
  AND b.bbox.ymin <= 42.36  AND b.bbox.ymax >= 42.34
  AND p.bbox.xmin <= -71.05 AND p.bbox.xmax >= -71.07
  AND p.bbox.ymin <= 42.36  AND p.bbox.ymax >= 42.34
LIMIT 10;

Nearest features to a point

Amenities within 500 m of Times Square. The bbox prefilter (about 1 km, deliberately looser than the radius) prunes row groups; ST_Distance_Sphere gives the exact cutoff in metres but, used alone, would force a full scan.

WITH origin AS (SELECT ST_Point(-73.9857, 40.7484) AS pt)
SELECT p.amenity, p.name,
       ST_Distance_Sphere(p.geometry, o.pt) AS dist_m
FROM read_parquet(url('US-NY', 'pois')) p, origin o
WHERE p.bbox.xmin <= -73.9757 AND p.bbox.xmax >= -73.9957   -- prunes row groups
  AND p.bbox.ymin <= 40.7584 AND p.bbox.ymax >= 40.7384
  AND ST_Distance_Sphere(p.geometry, o.pt) < 500            -- exact, metres
ORDER BY dist_m
LIMIT 15;

Several states at once

Pass a list of URLs and group by the state_iso column every file carries:

SELECT state_iso, count(*) AS pois
FROM read_parquet([
  url('US-NY', 'pois'), url('US-MA', 'pois'),
  url('US-CT', 'pois'), url('US-RI', 'pois')
])
GROUP BY state_iso
ORDER BY pois DESC;

GMWID: the high-voltage grid of a state

GMWID, the GeoMermaids World Infrastructures Dataset, covers the whole world in one file per layer, with voltages already parsed into kV and line lengths in km. Every row carries its country and state (a slug of the state's name), and the rows are Hilbert-sorted, so filtering on them skips most of the file:

SELECT voltage_kv, count(*) AS lines, round(sum(length_km)) AS km
FROM read_parquet('https://parquetry.geomermaids.com/gmwid/latest/power_line.parquet')
WHERE country = 'US' AND state = 'florida'
  AND lifecycle = 'active' AND voltage_kv >= 100
GROUP BY ALL
ORDER BY voltage_kv DESC;

2. Queries across the whole catalogue

"Every airport in North America" needs a wildcard across files, and a wildcard needs a directory listing, which plain HTTPS cannot give. A small read-only S3-compatible endpoint, s3.geomermaids.com, serves the same bucket with just enough of the S3 API (ListObjectsV2 and ranged GetObject) for DuckDB to expand globs. Anonymous, no signing.

Setup

Empty keys make httpfs send unsigned requests. Once per session:

INSTALL httpfs; LOAD httpfs;
INSTALL spatial; LOAD spatial;

SET s3_endpoint = 's3.geomermaids.com';
SET s3_url_style = 'path';
SET s3_use_ssl = true;
SET s3_access_key_id = ''; SET s3_secret_access_key = '';

Every aeroway feature in North America

About 100 files in one query. Row counts live in the Parquet footer, so DuckDB answers without reading a single data page:

SELECT count(*) AS features
FROM read_parquet('s3://parquetry/osm/latest/country=*/state=*/aeroways.parquet');

Top 10 states and provinces for airports

Wildcard the path, group by state_iso: the column is inside every file, no path parsing.

SELECT state_iso, count(*) AS aerodromes
FROM read_parquet('s3://parquetry/osm/latest/country=*/state=*/aeroways.parquet')
WHERE aeroway = 'aerodrome'
GROUP BY state_iso
ORDER BY aerodromes DESC
LIMIT 10;

One country only

Pin a path segment to a literal and the glob prunes at list time. Only Canadian files are read:

SELECT state_iso, count(*) AS stations
FROM read_parquet('s3://parquetry/osm/latest/country=CA/state=*/public_transport.parquet')
WHERE railway = 'station'
GROUP BY state_iso
ORDER BY stations DESC;

Wind turbines across the continent

The power theme is small, so a continent-wide scan is cheap:

SELECT country, count(*) AS turbines
FROM read_parquet('s3://parquetry/osm/latest/country=*/state=*/power.parquet')
WHERE power = 'generator'
  AND tags['generator:source'] = 'wind'
GROUP BY country
ORDER BY turbines DESC;

3. Outside DuckDB

GeoPandas

import geopandas as gpd

gdf = gpd.read_parquet(
    "https://parquetry.geomermaids.com/osm/latest/country=US/state=US-RI/buildings.parquet"
)
print(f"{len(gdf):,} Rhode Island buildings")
print(gdf[["building", "name", "height"]].head())

QGIS

QGIS 3.28 LTR or newer (GDAL 3.5 or newer with the Parquet driver). In Data Source Manager → Vector, prefix the URL with /vsicurl/ so GDAL streams it with range requests instead of downloading the whole file. QGIS opens one file at a time; for catalogue-wide questions, use DuckDB.

/vsicurl/https://parquetry.geomermaids.com/osm/latest/country=US/state=US-NY/buildings.parquet

4. Ask an AI agent

The parquetry skill packs these recipes, the layout of every dataset and the method for combining them, so an AI agent can answer a spatial question itself: What is the predominant land use in Hérault? How many buildings in Eastern Massachusetts are in the 100-year floodplain? It chooses the files, aligns the coordinate systems, runs the query, and states its definitions, coverage gaps, data dates and credits. In Claude Code:

/plugin marketplace add gsueur/parquetry-skills
/plugin install parquetry@geomermaids

The skill is plain Markdown with queries tested weekly against the live data, so any agent that can run DuckDB can use it.

Next

What each dataset holds, its URL layout and its licence are on geoparquet.geomermaids.com. How the files are written for fast reads is in the GeoParquet Writing cookbook. Need another region or a custom schema? Get in touch.