First Look at a Raster File: Dimensions, Bands, and NoData
A vector file’s first look answers “how many features, what shape, what CRS.” A raster’s is a different set of numbers entirely: how big is the grid, how many bands does each pixel carry, what does one pixel cover on the ground, and which value means “no data here” rather than a real measurement.
Get those wrong and everything downstream is wrong too — a slope calculation that treats -9999 as a very steep cliff, a mean elevation dragged down by a border of NoData pixels, a band mistaken for degrees Celsius when it’s really reflectance times 10,000.
Just want the summary, without the rest of the reading? Use the standalone tool — same engine, less prose.
1. Upload your file
A single .tif or .tiff — no sidecars, unlike a shapefile.
2. Why start with a summary?
Eight numbers, each answering a question that’s otherwise easy to get wrong by assumption:
- Dimensions — width × height in pixels. Determines how much work any pixel-by-pixel operation actually is, and whether a “small” file is small in bytes but enormous in pixel count (or the reverse).
- Bands — one grid of numbers, or several stacked together (RGB, a multispectral stack, a time series). Some checks and most visualizations only make sense once you know how many there are.
- CRS — same coordinate reference system concept as a vector file, same convention here: a missing CRS is assumed WGS84 and flagged, never silently treated as CRS-less.
- Resolution — the ground distance one pixel actually covers, in the file’s own CRS units. A 30 (Landsat-scale) versus a 0.001 (sub-meter) changes what questions the data can even answer.
- Data type —
uint8,int16,float32, … . The single most common source of a “why are all my values 0 or wildly wrong” surprise: an integer type storing values that were meant to be divided by a scale factor, or a float column silently truncated by being written back as an integer type. - NoData — the sentinel value marking “no measurement here” (often
-9999,0, or a type’s max value) — as fundamental to a raster as a null is to a table column, and just as easy to accidentally treat as a real number if it isn’t checked first. - Bounds — the same bounding-box sanity check as a vector file: numbers wildly outside -180..180 / -90..90 are the CRS-mismatch tell, here too.
- Value range — the per-band min/max/mean, with NoData pixels correctly excluded. The fastest way to catch a unit mistake (a “temperature in Celsius” band whose values run from 200 to 320 is actually Kelvin) before it reaches an analysis.
4. Inspect: summarize your file
Run the same summary in Python or R on the current data (your file uploaded above, or the built-in example below).
5. Same spatial question, two languages
Python and R read the same file through the same underlying GDAL, but the two engines disagree in ways a vector file’s sf/GeoPandas split never surfaces:
Python (rasterio)
src.width, src.height # dimensions
src.count # band count
src.crs # CRS
src.res # pixel resolution
src.dtypes[0] # data type, as declared in the file
src.nodata # the NoData sentinel value itself
src.read(i, masked=True) # one band, NoData pixels masked outR (stars)
st_dimensions(r) # dimensions + band count
sf::st_crs(r)$input # CRS
st_dimensions(r)$x$delta # pixel resolution
typeof(r[[1]]) # R's storage type, not the file's declared type
# no direct equivalent — see below
r[[1]] # already has NoData pixels as NAThree genuine divergences, not just naming differences:
- CRS formatting — same issue as the vector article’s
st_crs()vs GeoPandas footnote:st_crs(r)$inputandstr(src.crs)don’t always print the same string for the same underlying CRS.1 - Data type —
starsalways stores pixel values as R doubles internally; it doesn’t expose the file’s own declared GDAL type (uint8,int16,float32, …) the way rasterio’sdtypes[0]does. The R summary above reports"double"for every raster, regardless of what’s actually on disk — a real limitation, not a rounding choice. - NoData retention —
read_stars()converts a file’s declared NoData value toNAautomatically at read time (confirmed: a-9999sentinel pixel never pollutesmin()/max()above), but it doesn’t keep the sentinel value itself accessible anywhere afterwards. rasterio does the opposite:masked=Trueexcludes NoData pixels from a calculation, butsrc.nodatastill tells you the raw number the file uses. That’s why the R card’s “NoData” row never shows anything but “None declared,” even on a file that clearly has one — it isn’t wrong,starsgenuinely doesn’t retain it this way.
1 A GeoTIFF’s own embedded CRS often resolves to a human-readable name in GDAL’s metadata (e.g. "WGS 84"), which R’s st_crs()$input reports directly — while an EPSG code assigned explicitly in code (as the built-in example above does) round-trips back as "EPSG:4326" on both sides. Worth checking which one you’re looking at before assuming a mismatch means an actual problem.
6. A raster with no declared CRS
The reusable cell pair above already builds a small synthetic hill with one NoData pixel whenever nothing has been uploaded — click ▶ Run on both languages in section 4 to see it. This button swaps in the same raster, minus the CRS, to see the warning row do its job.2
2 An unreferenced raster isn’t rare in practice — a sensor’s raw output, or a file exported by a tool that never wrote a CRS tag at all. Nothing about the pixel values is wrong in that case; only where they sit on the earth is unknown until someone supplies it, which is exactly the situation the warning exists to flag before a map silently plots it in the wrong place (or, for sf/GeoPandas, refuses to reproject it at all).
7. Where to next
A summary is a starting point:
- Just needed the numbers? The standalone Raster Inspector has the same summary, no reading required.
- Working with vector data instead? Start with First Look at a Geospatial File — the same idea, for GeoJSON and Shapefile.
- Need to combine or transform bands (an NDVI-style calculation, a reclassification)? A Calculator/Band Math tool is planned as the next tool in the Raster family.