A tpcpatch is the lifting of pcpatch. It mirrors tpcpoint in every regard, same subtypes, same accessors, same casts and operators, except that each instant carries an entire compressed batch of points instead of a single point. The bounding box is again a tpcbox, computed in O(1) per instant from the patch's embedded PCBOUNDS.
Construct a temporal pcpatch of instant subtype
tpcpatch(pcpatch,timestamptz) → tpcpatch
SELECT tempSubtype(tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz)); -- Instant
Construct a temporal pcpatch of sequence subtype
tpcpatchSeq(tpcpatch[]) → tpcpatch
tpcpatchSeq(tpcpatch[],text) → tpcpatch
SELECT numInstants(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)])), interp(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)])); -- 2 | Step
Construct a temporal pcpatch of sequence-set subtype
tpcpatchSeqSet(tpcpatch[]) → tpcpatch
SELECT numSequences(tpcpatchSeqSet(ARRAY[tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)])])); -- 1
All the generic temporal accessors apply, plus the tpcpoint-style pcid and SRID. In addition:
Return the number of points in the patch carried by the first or last instant
startNumPoints(tpcpatch) → integer
endNumPoints(tpcpatch) → integer
SELECT startNumPoints(tpcpatch(PC_Patch(PC_MakePoint(1, ARRAY[10.0, 20.0, 30.0])), '2024-02-01'::timestamptz)); -- 1
Return the total number of points across every instant's patch. Read directly from each patch's header, no decompression.
numPoints(tpcpatch) → bigint
SELECT numPoints(tpcpatchSeq(ARRAY[
tpcpatch(PC_Patch(ARRAY[PC_MakePoint(1, ARRAY[1.0, 1.0, 1.0]),
PC_MakePoint(1, ARRAY[2.0, 2.0, 2.0])]),
'2024-01-01'::timestamptz),
tpcpatch(PC_Patch(ARRAY[PC_MakePoint(1, ARRAY[3.0, 3.0, 3.0])]),
'2024-01-02'::timestamptz)]));
-- 3
Set-returning function: emits one row per (instant timestamp, point) by walking each instant's patch and decomposing it through pgPointCloud's in-memory C API. Useful for joining a tpcpatch column against per-point predicates that the bbox-level operators can't express. Cost is O(total points), one per-instant decompression plus one row emission per point.
points(tpcpatch) → setof (t timestamptz, point pcpoint)
SELECT t, point FROM points(tpcpatch(
PC_Patch(ARRAY[PC_MakePoint(1, ARRAY[1.0, 1.0, 1.0]),
PC_MakePoint(1, ARRAY[2.0, 2.0, 2.0])]),
'2024-01-01'::timestamptz));
-- ('2024-01-01', '01:0000000000000000000000F03F0000000000000000F03F00000000000000F03F'::pcpoint)
-- ('2024-01-01', '01:0000000000000000000000004000000000000000400000000000000040'::pcpoint)
See the section called “Per-point operations: what is and is not available” for the limits of these accessors and what is and is not available at per-point granularity.
Transform a tpcpatch to another subtype
tpcpatchInst(tpcpatch) → tpcpatch
tpcpatchSeq(tpcpatch,interp='step') → tpcpatch
tpcpatchSeqSet(tpcpatch) → tpcpatch
SELECT tempSubtype(tpcpatchSeq(tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz))); -- Sequence
Transform a tpcpatch to another interpolation
setInterp(tpcpatch,interp) → tpcpatch
SELECT interp(setInterp(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)], 'discrete'), 'step')); -- Step
Restrict a tpcpatch to (the complement of) a pcpatch value or a set of pcpatch values
atValue(tpcpatch,pcpatch) → tpcpatch
minusValue(tpcpatch,pcpatch) → tpcpatch
atValues(tpcpatch,pcpatchset) → tpcpatch
minusValues(tpcpatch,pcpatchset) → tpcpatch
-- The matching value is held up to the next instant; the closing bound -- is stored as a second instant. SELECT numInstants(atValue(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)))); -- 2 SELECT numInstants(minusValue(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)))); -- 1
Restrict a tpcpatch to the instants whose patch passes a coarse PCBOUNDS overlap test against the tpcbox, or remove them. Granularity is patch-level: each surviving instant keeps its pcpatch payload verbatim, with no per-point decompression. Because pgPointCloud's PCBOUNDS is 2D, the Z dimension of the box is ignored at this granularity. Returns NULL when the tpcbox's pcid does not match.
atTpcbox(tpcpatch,tpcbox,border_inc bool=TRUE) → tpcpatch
minusTpcbox(tpcpatch,tpcbox,border_inc bool=TRUE) → tpcpatch
SELECT numInstants(atTpcbox(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), tpcbox_xt(0, 0, 2, 2, tstzspan '[2001-01-01, 2001-01-03]', 1))); -- 1 -- The dropped patch stays present on the open interval before its instant, -- so the complement keeps two instants. SELECT numInstants(minusTpcbox(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), tpcbox_xt(0, 0, 2, 2, tstzspan '[2001-01-01, 2001-01-03]', 1))); -- 2
Per-point variant of the previous: each surviving instant carries a freshly-built pcpatch holding only the points inside (or outside, for minus) the tpcbox in 2D, and 3D when the box has a Z dimension. Instants whose patch filters to zero points are dropped. Slower than the coarse variant because every patch is decompressed and rebuilt; use when you need point-level fidelity.
atTpcboxFine(tpcpatch,tpcbox,border_inc bool=TRUE) → tpcpatch
minusTpcboxFine(tpcpatch,tpcbox,border_inc bool=TRUE) → tpcpatch
SELECT numInstants(atTpcboxFine(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), tpcbox_xt(0, 0, 1, 1, tstzspan '[2001-01-01, 2001-01-03]', 1))); -- 1
Restrict a tpcpatch to the points whose XY projection intersects (or does not intersect, for minus) a 2D geometry. Z is ignored. SRID compatibility between the patch schema and the geometry must be ensured by the caller.
atGeometry(tpcpatch,geometry) → tpcpatch
minusGeometry(tpcpatch,geometry) → tpcpatch
-- The polygon boundary touches the point (3 3) of the patch at -- 2001-01-02, so that patch survives both the at and the minus side. SELECT numInstants(atGeometry(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), geometry 'POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))')); -- 2 SELECT numInstants(minusGeometry(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), geometry 'POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))')); -- 1
Restrict a tpcpatch to the instants before or after a timestamp. The strict flag excludes the instant at the timestamp itself
beforeTimestamp(tpcpatch,timestamptz,strict bool=TRUE) → tpcpatch
afterTimestamp(tpcpatch,timestamptz,strict bool=TRUE) → tpcpatch
SELECT timeSpan(beforeTimestamp(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5)), '2001-01-03'::timestamptz)]), timestamptz '2001-01-02')); -- [2001-01-01, 2001-01-02) SELECT timeSpan(afterTimestamp(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5)), '2001-01-03'::timestamptz)]), timestamptz '2001-01-02')); -- (2001-01-02, 2001-01-03]
Insert a tpcpatch into another one, or update another one with it
insert(tpcpatch,tpcpatch,connect bool=TRUE) → tpcpatch
update(tpcpatch,tpcpatch,connect bool=TRUE) → tpcpatch
SELECT numInstants(insert(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5)), '2001-01-03'::timestamptz)]))); -- 3
Delete a time selector (timestamp, set, period, or spanset) from a tpcpatch
deleteTime(tpcpatch,timestamptz,connect bool=TRUE) → tpcpatch
deleteTime(tpcpatch,tstzset,connect bool=TRUE) → tpcpatch
deleteTime(tpcpatch,tstzspan,connect bool=TRUE) → tpcpatch
deleteTime(tpcpatch,tstzspanset,connect bool=TRUE) → tpcpatch
-- Deleting the middle instant splits the sequence into
-- {[2001-01-01, 2001-01-02), (2001-01-02, 2001-01-03]}; each half stores
-- its bound at the deleted timestamp, giving four instants.
SELECT numInstants(deleteTime(tpcpatchSeq(ARRAY[
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5)), '2001-01-03'::timestamptz)]), timestamptz '2001-01-02'));
-- 4
Append an instant or a sequence to a tpcpatch
appendInstant(tpcpatch,tpcpatch) → tpcpatch
appendSequence(tpcpatch,tpcpatch) → tpcpatch
SELECT numInstants(appendInstant(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5)), '2001-01-03'::timestamptz))); -- 3
Return the distinct values of a tpcpatch with the span set on which each of them is taken
unnest(tpcpatch) → {(value,time)}
SELECT (un).time
FROM (SELECT unnest(tpcpatchSeq(ARRAY[
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-01'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5), pcpoint(1, 6, 6, 6)), '2024-01-02'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-03'::timestamptz)])) AS un) t;
-- {[2024-01-02, 2024-01-03)}
-- {[2024-01-01, 2024-01-02), [2024-01-03, 2024-01-03]}
Split a tpcpatch into fragments, one per time bin
timeSplit(tpcpatch,bin_width interval,origin timestamptz='2000-01-03') → {(time,tpcpatch)}
SELECT (ts).time, numInstants((ts).temp) FROM (SELECT timeSplit(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5), pcpoint(1, 6, 6, 6)), '2024-01-02'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-03'::timestamptz)]), interval '1 day', '2024-01-01'::timestamptz) AS ts) t; -- 2024-01-01 | 2 -- 2024-01-02 | 2 -- 2024-01-03 | 1
Return the array of time spans of a tpcpatch's segments
spans(tpcpatch) → tstzspan[]
SELECT spans(tpcpatchSeq(ARRAY[
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-01'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5), pcpoint(1, 6, 6, 6)), '2024-01-02'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-03'::timestamptz)]));
-- {"[2024-01-01, 2024-01-02]","[2024-01-02, 2024-01-03]"}
Return the time spans of a tpcpatch split into a given number of bins, or with a given number of segments per bin
splitNSpans(tpcpatch, integer) → tstzspan[]
splitEachNSpans(tpcpatch, integer) → tstzspan[]
SELECT splitNSpans(tpcpatchSeq(ARRAY[
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-01'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 5, 5, 5), pcpoint(1, 6, 6, 6)), '2024-01-02'::timestamptz),
tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2024-01-03'::timestamptz)]), 2);
-- {"[2024-01-01, 2024-01-02]","[2024-01-02, 2024-01-03]"}
Return true iff at least one point in any of the tpcpatch's instants intersects the geometry (XY only).
eIntersects(tpcpatch,geometry) → boolean
SELECT eIntersects(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), geometry 'POINT(1 1)'); -- t
The same bbox operator surface as the section called “Bounding-box operators” applies to tpcpatch. The predicate evaluates against the value's tpcbox, computed in O(1) per instant from the patch's embedded PCBOUNDS.
Topological predicates: &&, @>, <@, ~=, -|-. See the section called “Topological Operators”.
Strict directional predicates on the X / Y / Z / time axes (<<, >>, <<|, |>>, <</, />>, <<#, #>>) and their "overlaps-or-X" variants (&<, &>, &<|, |&>, &</, /&>, &<#, #&>). See the section called “Position Operators”.
Nearest-approach distance (|=|) is KNN-orderable through the GiST opclass.
nearestApproachDistance(tpcpatch,tpcbox) → float
nearestApproachDistance(tpcpatch,tpcpatch) → float
Test whether the value at any (ever) or every (always) instant equals, or differs from, a pcpatch or another tpcpatch
eEq(pcpatch,tpcpatch) → boolean
eEq(tpcpatch,pcpatch) → boolean
eEq(tpcpatch,tpcpatch) → boolean
aEq, eNe and aNe take the same three argument pairs
pcpatch ?= tpcpatch → boolean
tpcpatch ?= pcpatch → boolean
tpcpatch ?= tpcpatch → boolean
The operators %=, ?<> and %<> take the same three operand pairs
SELECT eEq(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)])); -- t SELECT aEq(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2))); -- f
Return the temporal equality or inequality of a tpcpatch with a pcpatch or with another tpcpatch, as a tbool
tEq(pcpatch,tpcpatch) → tbool
tEq(tpcpatch,pcpatch) → tbool
tEq(tpcpatch,tpcpatch) → tbool
tNe takes the same three argument pairs
pcpatch #= tpcpatch → tbool
tpcpatch #= pcpatch → tbool
tpcpatch #= tpcpatch → tbool
The operator #<> takes the same three operand pairs
SELECT tEq(tpcpatchSeq(ARRAY[ tpcpatch(pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2)), '2001-01-01'::timestamptz), tpcpatch(pcpatch(1, pcpoint(1, 3, 3, 3), pcpoint(1, 4, 4, 4)), '2001-01-02'::timestamptz)]), pcpatch(1, pcpoint(1, 1, 1, 1), pcpoint(1, 2, 2, 2))); -- [t@2001-01-01, f@2001-01-02]
The tpcpatch_btree_ops opclass produces a total order over tpcpatch values, but the order is not spatial. The comparator on the underlying pcpatch is byte-wise (memcmp over the meaningful varlena bytes); the temporal-level comparator is the lexicographic combination of per-instant timestamps and per-instant pcpatch byte order. The observable consequences:
pcid is the primary discriminator. A patch with a smaller pcid sorts before one with a larger pcid, regardless of point coordinates, count, or compression. This makes ORDER BY on a mixed-pcid column group rows by schema first, which is sometimes useful as a coarse-grained "chunk by source schema" pass.
Within a single pcid, ordering follows the on-disk byte layout: compression scheme, then npoints, then the 2D PCBOUNDS (xmin, xmax, ymin, ymax), then the compressed data payload. This is well-defined and stable for a given pgPointCloud release, but it is not a geometric or spatial ordering, two patches whose bounding boxes overlap in 3D space can sort arbitrarily relative to each other if their compression scheme or point count differs.
Equality is exact-bytes equality over the meaningful bytes (the trailing zero-padding pgPointCloud's varlena reserves is excluded). Two patches built from the same point set in the same order, with the same compression, are equal; reordering the points or changing compression makes them unequal.
For spatial-meaningful ordering, use the GiST KNN operator |=| (see nearest-approach distance on tpcpatch); for time-meaningful ordering, project to startTimestamp or timespan and order on that.
Operations on tpcpatch values come at two granularities: patch-level and per-point. The split matters because pgPointCloud patches store their points in a compressed payload that the bbox layer cannot inspect.
Patch-level, supported. Each instant's patch is treated as opaque and is kept or dropped as a whole, using only the 4-double PCBOUNDS header (xmin / xmax / ymin / ymax) and the instant's timestamp. This is the granularity of atTpcbox / minusTpcbox and the bounding-box operators in the section called “Bounding-box operators”, and of the bbox-driven tpcbox aggregate. No payload decompression happens; queries are O(number of instants).
Because PCBOUNDS is 2D, the Z dimension of any tpcbox argument is ignored at this granularity even when the underlying schema has a Z dimension.
Per-point inspection, supported. The points set-returning function emits one row per (instant timestamp, point) by decomposing each instant's patch through pgPointCloud's in-memory C API. Cost is O(total points), one per-instant decompression plus one row emission per point.
Per-point filtering / construction, supported. atTpcboxFine / minusTpcboxFine, atGeometry / minusGeometry, and eIntersects(tpcpatch, geometry) walk every point of every instant in C, applying their predicate against the actual point coordinates rather than just the patch bounding box. Surviving instants carry a freshly-built patch holding only the points that passed; instants whose patches filter to zero points are dropped.
The recommended workflow when both granularities are available is: (a) prune at the patch level with atTpcbox or an SP-GiST/GiST index scan to drop entire instants whose PCBOUNDS doesn't overlap the area of interest, and (b) refine on the survivors with atTpcboxFine / atGeometry when point-level fidelity is needed.