Skip to content

About point clouds

You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.

A point cloud is the rawest thing the v2 API stores: a set of 3D points, each one a place a laser pulse struck something and came back. It is not yet a canopy, not yet a tree, not yet fuel — it is a measurement of surfaces, and everything FastFuels does with it afterwards is interpretation.

That makes it different in kind from the other fuel resources. A grid is a fixed lattice with a value in every cell. An inventory is a list of discrete objects with attributes. Both are models of the landscape — someone has already decided what the data means. A point cloud has no cells, no objects, and no semantics beyond “a return happened here.” Its structure is whatever the scanner’s geometry produced, and the work of turning it into fuel is still ahead of you.

This page is about the idea: what the resource holds, why it exists as a first-class thing rather than a hidden step, how the two acquisition platforms differ, and where a cloud sits in the pipeline. It is not a walk-through of any request. It also does not re-teach how tree detection from a CHM works — that page picks up exactly where this one leaves off, at the height surface a cloud is rasterized into.

A point cloud could reasonably have been a hidden implementation detail — a file you hand in at the start of a “make me fuel from lidar” call and never see again. That is roughly how v1 worked: one scan per domain, one pipeline, a fuel grid out the other end, and no way to hold, inspect, or reuse anything in between.

Making the cloud a resource with its own identity is what lets that single operation come apart into a composition of steps that already existed independently:

3DEP ──┐
├──▶ point cloud ──▶ canopy grid (chm) ──▶ tree inventory ──▶ voxel grid ──▶ export
upload ─┘

Each arrow is a request you make, inspect, and can make differently. You can rasterize one cloud into CHMs at two resolutions and compare them. You can detect trees from the CHM with a fixed window, then again with a variable one. You can hold onto an expensive 3DEP fetch and build a dozen derived products from it without re-downloading a byte. None of that is possible when the whole chain is one call.

The second reason is standardization. Lidar arrives in wildly heterogeneous shapes — different CRSs, different classification conventions, different point formats, different vendors’ ideas of what “unclassified” means. Ingestion normalizes what it can (everything is reprojected to the domain CRS) and reports what it cannot (the ASPRS classes actually present are recorded, not assumed). A downstream step then works against a known shape rather than guessing.

A point cloud is created either by fetching public lidar or by uploading your own. Both are asynchronous, both produce the same resource, and both record how they happened in a source field carrying a name discriminator.

The 3D Elevation Program is the USGS’s national airborne lidar collection, published as an open catalog of Entwine Point Tile — octree-indexed — datasets. Requesting a 3DEP cloud reads that catalog, works out which acquisitions cover your domain, clips their points to it, reprojects them to the domain CRS, and stores the result. Because 3DEP is airborne by definition, the cloud is always als; there is no acquisition type to choose.

3DEP is published as separate surveys, not as one seamless layer, and this is the thing worth understanding about it. Surveys overlap, differ in age and point density, and have irregular boundaries that follow flight lines and project areas rather than anything on the ground. So a domain is very often covered to 99-point-something percent rather than exactly 100, and a domain that straddles two surveys can be filled by a mosaic with a seam between flights of different dates and densities running through it.

By default the backend prefers a single survey that covers the whole domain, and otherwise combines the fewest that fill it. You can override that by pinning datasets explicitly — to force a higher-density or more recent survey where several overlap. What was actually used is written back onto the cloud:

source fieldWhat it records
datasetsthe acquisitions read, in the order they were used
requested_datasetswhat you pinned, or null if the backend chose
coverage_fractionthe fraction of the domain the cloud covers
catalog_fetched_onwhen the acquisition catalog was read

That last field exists because the catalog is not static. USGS publishes new surveys, so an identical request made a year apart can legitimately select different data — recording the read time is what makes a fetch reconstructible rather than merely repeatable.

A coverage endpoint answers all of this before you commit to anything. It is a catalog lookup and some geometry, not a data read, so it returns immediately and tells you what is available, what each survey would contribute, roughly how many points a fetch would return, and whether that exceeds the per-fetch budget (200 million points by default). Checking it first is the difference between learning about a coverage gap in a second and learning about it after a long background job.

The alternative is to bring your own scan: an uncompressed LAS or compressed LAZ file, including Cloud Optimized Point Clouds, which are valid LAZ. The format is detected from the file itself; there is nothing to declare. This is the only way to get a tls cloud into the system, and the way to use commercial or research lidar that is not in 3DEP.

Uploading is a two-step flow, the same one the other v2 upload endpoints use. The create request returns a signed URL and a set of headers, and you PUT your file to that URL. The headers are not advisory — the signed URL commits to them, and an upload that omits or alters one is rejected by storage before the API ever sees it. The URL is good for an hour, and the file may not exceed 1 GiB.

One requirement catches people out: the file must carry a coordinate reference system, and a file without one is rejected during ingestion, not at upload time. The upload will succeed and the cloud will then fail, with the reason on its error field. A CRS that merely differs from the domain’s is fine — that gets reprojected.

Every cloud carries a type recording the platform it was captured from, and it is the most consequential field on the resource, because the platform determines what the scan can physically see.

als — airbornetls — terrestrial
Captured froman aircraft or drone, looking downa tripod on the ground, looking out and up
Coverslarge areas, whole landscapesa single plot, tens of metres across
Resolves wellthe canopy surface — the top of the crownstems and sub-canopy — trunks, branches, ladder fuels
Resolves poorlyanything under the dominant crownsanything beyond the immediate plot, or above dense foliage
Available from3DEP or uploadupload only

The complementarity is exact, and it is not an accident: each platform is blind in the direction the other one sees. An airborne scan images the canopy from above, so the first thing it hits is the outer crown surface and the last thing it reaches is the understory. A terrestrial scan stands underneath, so it resolves the trunk, the branch structure, and the ladder fuels in beautiful detail — and sees the top of the canopy through whatever foliage happens to be in the way, over an area the size of a plot.

That is why ALS is what the fuel pipeline is built on. Canopy height models, individual tree detection, and everything downstream of them assume a scan of the landscape from above.

Once ingestion completes, a cloud carries a summary — three numbers that let you judge it without downloading a single point.

point_count is the total number of points. density is the average over the cloud’s horizontal extent, in points per square metre. Density is the number most worth reading, because it sets the ceiling on everything downstream. Read it against the cell size you intend to rasterize at: density × cell area is roughly how many returns land in a cell, and a cell that receives one or two is being described by luck rather than by measurement. Public 3DEP acquisitions commonly run in the range of a few points per square metre, which supports the 1 m default comfortably and finer cells much less so. No downstream parameter recovers detail the scan never captured.

point_classes lists the ASPRS standard classification codes present, sorted ascending. The classification is a per-point label the vendor assigned — not something FastFuels computes — and the codes that matter here are:

CodeMeaning
0never classified
1unclassified (processed, not assigned)
2ground
3, 4, 5low, medium, and high vegetation
6building
7, 18noise
9water

Reading point_classes is how you predict the quality of a derived canopy height model before you build one, and the single code to look for is 2. Its presence or absence decides whether the ground beneath your canopy is measured or inferred — which is the subject of the next section.

The classes also explain a trap. Many 3DEP acquisitions classify only ground and unclassified, leaving vegetation in class 1; a genuinely unprocessed upload may be entirely class 0. So point_classes: [1, 2] does not mean the cloud has no vegetation — it means the vendor did not label it. FastFuels reads classes 0 through 5 as potential canopy for exactly this reason, deliberately excluding noise, water, and buildings where those are labeled. Where they are not labeled, they are still in the data, and they still appear in the canopy surface.

This is the idea most worth taking away from this page, and it is easy to miss because it is arithmetic rather than remote sensing.

A canopy height is not a measurement. It is a difference — the elevation of a canopy return minus the elevation of the ground beneath it. The scanner measures elevations; the ground surface has to be established separately, and every canopy height inherits its error.

When a cloud carries ASPRS class 2, that ground is measured: the class 2 returns are the surface. When it does not, the surface is derived from the data with a progressive morphological filter, and the result is genuinely good in forested terrain — but it degrades wherever there is a wide contiguous area with no ground returns at all. Two situations produce exactly that: a large building footprint, and closed evergreen canopy so dense that no pulse reaches the floor.

Rather than leaving this invisible, the derived canopy grid reports it. A CHM built from a point cloud records, under source.ground:

  • ground_sourceclassification if the cloud’s own class 2 returns were used, derived if the surface was inferred.
  • ground_coverage — the fraction of cells that contain at least one ground return. Heights over cells far from any ground return rest on an interpolated surface.
  • max_ground_distance_m — how far the worst cell is from the nearest ground return. A large value localizes the problem: it means somewhere in your domain there is a wide void the ground surface had to be guessed across.

A CHM with ground_source: derived and low ground_coverage is not wrong, but it is a weaker claim than the same raster built on measured ground, and nothing downstream will remind you. Detection will happily place trees on interpolated heights, and voxelization will happily build crowns from them. Read these fields once, when the grid completes; they are the only place the uncertainty is visible.

Georeference, and what reprojection does and does not touch

Section titled “Georeference, and what reprojection does and does not touch”

A completed cloud carries a georeference with two things: a crs as an authority code, and bounds as a flat six-element box — [min_x, min_y, min_z, max_x, max_y, max_z]. Point clouds are three-dimensional, so the box has a vertical extent, which most other resources’ bounding boxes do not.

The crs is always the domain’s CRS. This is worth stating because grids behave differently: for a raster, reprojection means resampling — inventing values at new pixel centres — so a mismatched grid is rejected rather than silently degraded. For points there is no such cost. Reprojecting a point is an exact per-point coordinate transform with nothing interpolated, so a mismatched upload is simply reprojected on ingest and stored in the domain frame.

bounds is also how you sanity-check coverage. Comparing the cloud’s horizontal extent against the domain’s is the cheapest way to notice that a fetch came back short, and it is independent of coverage_fraction, which is derived from catalog metadata rather than from the points themselves.

Lifecycle: what changes, and what never does

Section titled “Lifecycle: what changes, and what never does”

Creating a point cloud is asynchronous. The request returns immediately with status: "pending", and the cloud walks pending → running → completed, or failed. Ingestion is a background job because it is genuinely expensive — a 3DEP fetch reads and clips potentially hundreds of millions of points over the network — so nothing is gained by making you wait on an open connection.

While the cloud is pending or running, georeference and summary are null. They are not partially filled and then completed; they simply do not exist until the backend has read the bytes. Any code that reads density or bounds has to check status first.

Once completed, the content is immutable. There is no endpoint that edits points. What you can change is metadata — name, description, tags — and a metadata update deliberately does not move the checksum.

That distinction is the whole point of the checksum. It is an opaque version marker (a random hex string, not a hash of the content), and its only job is to change when the content does. Derived resources record the value they were built from — a CHM grid stores source_point_cloud_checksum — so comparing that stored value against the cloud’s current checksum tells you whether the thing you built from has changed underneath you. The API does not act on that comparison: there is no stale flag, no warning, and nothing is blocked or rebuilt for you. Staleness detection is yours to do, and the checksum is the tool for doing it.

Two more lifecycle facts worth knowing:

  • Point clouds count against your quotas — there are limits on how many can be processing at once, how many you may hold in total, how much storage they occupy, and how many you may create per week. The count limits are the tightest of any resource: an order of magnitude fewer clouds than grids, and the fewest concurrent jobs of anything in v2, because a single fetch can move a very large amount of data. Plan a lidar workflow around building many derived products from one cloud rather than many clouds. See About quotas.
  • They are children of their domain. Deleting a domain that still holds point clouds fails with a 412 unless you pass force=true, in which case the clouds are deleted along with it. A cloud deleted on its own takes its stored points with it; derived grids and inventories are not deleted and continue to work, since they hold their own data — but the checksum they recorded now refers to something that no longer exists.

A cloud is a measurement, not a representation

Section titled “A cloud is a measurement, not a representation”

FastFuels describes fuel three ways — as a continuous field (a grid), as a statistical group (a layerset), and as discrete objects (an inventory). Those are laid out in About inventories, and a point cloud is none of them. It is the measurement those representations are built from, and the gap between the two is not a formatting problem.

A fire model needs to know how much fuel is in a volume of space, what kind it is, and how it is arranged. A point cloud knows none of that. It knows that a pulse returned from a location. It does not know whether that return came from a branch, a leaf, a wire, a roof, or a rock; it does not know how much mass is there; and it does not know what is in the space between returns, which is most of the volume. Points are a sample of surfaces, and fuel is a property of volumes.

Bridging that gap is what the pipeline does, and each step trades resolution for meaning:

  1. Points → a height surface. Rasterizing the cloud collapses it to one number per cell — the greatest height above ground of any return in it. You throw away the vertical distribution and gain a continuous, comparable surface that the detection algorithms can work on.
  2. A height surface → discrete trees. Detection reads the peaks of that surface as treetops, producing an inventory with x, y, and height. You throw away the surface and gain objects — things with identity, which can be filtered, thinned, and modeled individually.
  3. Trees → a 3D fuel grid. Voxelization gives each tree a modeled crown and distributes biomass through it. You throw away the assumption that a tree is a point and gain the thing a physics-based fire model actually consumes: bulk density in every cell of a lattice.

Notice what happens across those three steps. Fidelity to the measurement drops monotonically — by the end, the only thing surviving from the original scan is each tree’s position and height. Everything else in the final fuel grid is modeled: crown shape from an allometric profile, biomass from an equation in species and diameter, and species and diameter themselves imputed from height. A lidar fuelbed is measured geometry filled in with modeled mass, and knowing which parts are which is the difference between using it well and trusting it too far.

The honest limits, collected:

  • A cloud is not exportable. There is no endpoint that hands your points back as a file. Export a derived grid or inventory instead. The cloud is an input, not a deliverable.
  • Sub-canopy structure cannot be recovered from an airborne scan. Canopy base height, ladder fuels, and bulk density are properties of what is underneath the crown surface, and ALS resolves that poorly by construction. They have to be imputed from tree records, not measured. This is the same limit tree detection runs into, seen one step earlier.
  • Fidelity includes things that are not vegetation. A surface model records what is physically there. Where a vendor has not classified buildings and wires — and many acquisitions classify only ground — a roofline or a transmission conductor appears in the canopy surface as tall “vegetation” and will be detected as trees. A modeled canopy product has no such artifact, because it has no such fidelity.
  • There is no direct cloud → inventory shortcut. Detection consumes a CHM grid, not points, and this is deliberate: the intermediate raster is where resolution, alignment, and ground quality become visible and adjustable. Going straight from points to stems would hide all three.
  • TLS has no consumer. See above.
  • About grids — the raster a cloud is rasterized into, and the lattice that makes derived products comparable.
  • How tree detection from a CHM works — what happens to the height surface once you have it, and the over- versus under-detection trade-off you tune.
  • About inventories — the discrete trees a CHM becomes, and why a detected inventory needs GDAM allometry before it can be voxelized.
  • About domains — the spatial frame a point cloud attaches to, and the source of the CRS it is reprojected into.
  • About quotas — the limits point clouds consume, and why the count limits bind before the storage ones do.

The procedures — checking 3DEP coverage, fetching a cloud, uploading your own, inspecting one, and rasterizing it into a canopy height model — are covered by the point-cloud how-to guides.