Cloud GIS in Practice: COG, GeoParquet and Reading Only What You Need
What cloud-native geospatial formats are, how to convert a raster to a Cloud Optimized GeoTIFF, and how to read just a small window from it instead of downloading the whole file.
A typical desktop GIS workflow downloads a file and opens it. That works for a 50 MB shapefile. It works badly for a 2 GB satellite scene when you need a 5 km square of it.
Cloud GIS often gets described as "hosting your GIS on someone else's servers". The more useful change is in how data is stored, so that a program can fetch a small part of a large file over the network.
What is cloud GIS?
Cloud GIS covers a few different things, and it helps to separate them.
- Cloud storage holds files in object storage such as Amazon S3, Google Cloud Storage or Azure Blob Storage. You request a file by its URL.
- Managed databases run something like PostgreSQL with PostGIS for you, so you do not maintain the server.
- Cloud-native formats are file layouts designed so you can read part of a file without reading all of it.
This post focuses on the third, because it changes how you work most.
Why it matters
A normal GeoTIFF can be read in pieces too, but the pieces are not always laid out in a way that makes this efficient over a network. Cloud-native formats fix that. The result is less data transferred, faster maps and analyses on large data, and no need to copy whole archives before you start.
How it works
Two formats cover most day-to-day use.
Cloud Optimized GeoTIFF (COG) is a regular GeoTIFF with a specific internal layout. The image is split into tiles, and overview levels (reduced-resolution copies) are stored in the same file. A reader asks the server for a byte range, using an HTTP range request, and gets just the tiles it needs. The header says where each tile is.
GeoParquet stores vector data in the Parquet column format. It is compact, quick to filter by column, and works well with tools like GeoPandas and DuckDB. It is a good choice for large tables of features that you analyse rather than edit.
A third piece, STAC (SpatioTemporal Asset Catalog), is a standard way to describe collections of imagery so you can search by place and date and get back links to COG files. Many public satellite archives publish a STAC catalogue.
Practical GIS example
You have a large GeoTIFF of a Sentinel-2 band, and you need NDVI for one village. You want to convert it once to a COG, then read only the area around the village.
Workflow
- Convert to COG with GDAL. GDAL has a COG driver from version 3.1.
# Convert a GeoTIFF to a Cloud Optimized GeoTIFF.
# Step 1: Run this in a terminal where GDAL is installed
gdal_translate input.tif output_cog.tif -of COG -co COMPRESS=DEFLATE
-
Check it.
gdalinfo output_cog.tiflists the layout, including the overviews. -
Read a window with Rasterio. The same code works on a local file or a URL that supports range requests.
# Read a small window from a (possibly remote) Cloud Optimized GeoTIFF.
import rasterio
from rasterio.windows import from_bounds
# Step 1: A local path, or an https:// URL to a COG
path = "output_cog.tif"
with rasterio.open(path) as src:
# Step 2: The area we want, in the raster's own CRS (xmin, ymin, xmax, ymax)
xmin, ymin, xmax, ymax = 380000, 2020000, 385000, 2025000
# Step 3: Turn those coordinates into a pixel window
window = from_bounds(xmin, ymin, xmax, ymax, transform=src.transform)
# Step 4: Read only that window
data = src.read(1, window=window)
print("CRS:", src.crs)
print("Window shape:", data.shape)
print("Whole raster shape:", (src.height, src.width))
The bounds in step 2 must be in the raster's CRS, not latitude and longitude, unless the raster itself is in EPSG:4326. Print src.crs first and reproject your bounds if needed. A mismatch gives you an empty or wrong window.
- For vector data, try GeoParquet.
# Save a layer as GeoParquet and read it back.
# pip install geopandas pyarrow
import geopandas as gpd
# Step 1: Read any vector file
gdf = gpd.read_file("buildings.gpkg")
# Step 2: Write to GeoParquet
gdf.to_parquet("buildings.parquet")
# Step 3: Read it back, loading only the columns you need
subset = gpd.read_parquet("buildings.parquet", columns=["geometry", "height"])
print(subset.head())
Compare the file sizes on your own data rather than trusting a general figure. The saving depends on the data.
Implementation approach for a team
- Keep dev, test and production separate so a bad change is not live.
- Put the raw data in cloud storage and keep it unchanged. Write derived products to a separate location.
- Automate deployments and database migrations rather than doing them by hand.
- Set up monitoring and alerts for the services people depend on.
- Control access with roles, and decide early who can read and who can write.
- Watch costs. Storage is cheap, but transferring data out of a cloud region and running always-on servers are the usual surprises.
Common mistakes
- Treating a COG as magic. A file converted without tiling or overviews, or served from a host that does not support range requests, will still be read in full.
- Moving everything at once. Start with one dataset and one workflow, and measure the difference.
- Forgetting the CRS. Moving data does not fix a missing or wrong CRS. The checks in the CRS post still apply.
- Ignoring egress and storage costs until the first invoice.
- No access plan. A public bucket exposes everything in it.
- Editing data in a read-optimised format. COG and GeoParquet are for reading and analysis. For data that many people edit, use a database.
Key takeaways
- The biggest practical change in cloud GIS is formats that allow partial reads.
- COG for rasters, GeoParquet for large vector tables, STAC to find data.
- GDAL converts, Rasterio reads windows, GeoPandas reads and writes GeoParquet.
- Plan environments, access and costs, not only the tools.
- Test on one dataset first and compare against your current workflow.

