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.
Why the resource exists
Section titled “Why the resource exists”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 ──▶ exportupload ─┘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.
Two ways in
Section titled “Two ways in”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.
USGS 3DEP
Section titled “USGS 3DEP”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 field | What it records |
|---|---|
datasets | the acquisitions read, in the order they were used |
requested_datasets | what you pinned, or null if the backend chose |
coverage_fraction | the fraction of the domain the cloud covers |
catalog_fetched_on | when 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.
Your own upload
Section titled “Your own upload”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.
ALS and TLS are not interchangeable
Section titled “ALS and TLS are not interchangeable”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 — airborne | tls — terrestrial | |
|---|---|---|
| Captured from | an aircraft or drone, looking down | a tripod on the ground, looking out and up |
| Covers | large areas, whole landscapes | a single plot, tens of metres across |
| Resolves well | the canopy surface — the top of the crown | stems and sub-canopy — trunks, branches, ladder fuels |
| Resolves poorly | anything under the dominant crowns | anything beyond the immediate plot, or above dense foliage |
| Available from | 3DEP or upload | upload 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.
What a cloud tells you about itself
Section titled “What a cloud tells you about itself”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:
| Code | Meaning |
|---|---|
0 | never classified |
1 | unclassified (processed, not assigned) |
2 | ground |
3, 4, 5 | low, medium, and high vegetation |
6 | building |
7, 18 | noise |
9 | water |
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.
Ground is half of a canopy height
Section titled “Ground is half of a canopy height”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_source—classificationif the cloud’s own class 2 returns were used,derivedif 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:
- 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.
- A height surface → discrete trees. Detection reads the peaks of that
surface as treetops, producing an inventory with
x,y, andheight. You throw away the surface and gain objects — things with identity, which can be filtered, thinned, and modeled individually. - 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.
What a point cloud cannot do
Section titled “What a point cloud cannot do”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.
Where to go next
Section titled “Where to go next”- 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.