Measurement result reference
Every field in a detection result, the category list, and the units each quantity uses.
View as MarkdownThis page describes the JSON returned by POST /v1/detect/full, POST /v1/detect/address and POST /v1/detect/tiles, and by GET /v1/detect/status/{job_id} under result. Saved measurements reuse the counter_summary shape; see Saved measurements & imagery.
Coordinates are [latitude, longitude] everywhere in JSON output. Fields are only ever added over time, so ignore fields you don't use.
Top-level fields#
| Field | Type | Description |
|---|---|---|
success |
boolean | true on a completed detection. |
summary |
object | Totals. See summary. |
parking_areas |
array | Paved areas with geometry. See parking_areas. |
counters |
object | Detected items by category, with geometry. See counters. |
counter_summary |
object | Every quantity by category. The best field to store. See counter_summary. |
imagery_source |
object | Which imagery was measured. See imagery_source. |
carports, concrete, sidewalk |
object | Present when you turned them on. See Optional layers. |
curbs, wheel_stops, site_objects, landscape |
object | Present when you turned on the matching add-on model. See Add-on models. |
areas |
array | Present when you sent named areas. See areas. |
excluded_islands |
object | Present when you sent islands to leave out. count (islands applied) and area_sqft (ground they take out of your boundary, in square feet). See Excluded islands. |
marking_details, striping_by_color |
object | Present when you sent marking_details: true. See Marking details. |
context |
object | captured_at (when this detection ran, UTC; not the imagery date) and map_view.bounds (the box measured). |
billing |
object | Partner keys only. |
The response also carries fields kept for compatibility with TruTec's embedded editor: contours, holes, parking, objects, predictions, preds, count, deducted_counts, measurement_types_by_object, measurement_entry, iframe_payload, striping_by_phase, custom_items, custom_items_summary, phase_colors, specs_breakdown, spec_definitions, value_overrides, lf_assumption_overrides and object_stats. They repeat data from the fields above in other shapes. New integrations can ignore them.
summary#
| Field | Type | Description |
|---|---|---|
total_area.value |
number | Total paved area in your unit, rounded to 2 decimals. Includes carport area when carports are on. |
total_area.unit |
string | SF, SY, SM or AC. |
total_area.formatted |
string | e.g. "186,420 SF". |
total_area_sqft |
integer | The total in square feet. |
total_stalls |
integer | stalls + handicap + trucks. |
polygon_count |
integer | Number of separate paved areas. |
unit |
string | The unit you requested, e.g. sf. |
phase |
string | Phase 1, or the first area's name when you sent areas. |
parking_areas#
| Field | Type | Description |
|---|---|---|
rings |
array | [outline, hole, hole, ...], each a list of [lat, lng]. Use this to draw the area with its holes (islands, buildings). |
coordinates |
array | The outline only (same as rings[0]). |
holes |
array | The holes only. |
area_sqft |
number | Area in square feet, net of holes. |
type |
string | Always parking_area. |
area, phase |
string | The area name, when you sent areas. |
counters#
One entry per detected category. Categories with nothing detected are left out. Every entry has label, unit, formatted, values, color, group, data_type, display_modes and, where relevant, assumptions. The top-level count or value shows the category's display value (see counter_display); values always has every quantity.
Markers (stalls, handicap, trucks, arrows, abc) list items in points[]:
{"id": "stalls-12", "lat": 30.40087, "lng": -97.72201, "confidence": 0.94, "area_sqft": 161.8,
"polygon": [[30.40085, -97.72203], [30.40085, -97.72199], [30.40089, -97.72199], [30.40089, -97.72203]]}
confidence (0 to 1), area_sqft and polygon appear when known. polygon is a simplified 4-corner box around the item; area_sqft is measured on the detected shape, so it won't exactly match the box.
Area markings (crosswalks, crosshatches, stop_line) list items in polygons[]: id, coordinates (one ring), area_sqft and confidence. Crosswalks add crosswalk_pattern (standard, continental, ladder or unknown), has_borders and has_bars. Hatching adds hatching_type (striped, solid or ada_adjacent), has_interior_stripes and is_ada_adjacent. Stop bars add bar_count.
Striping (lines_white, lines_yellow, lines_double_yellow) lists each stripe in lines[] as a centerline: id, points ([lat, lng] list), length_ft, phase and confidence, plus overlap_with_stalls_ft, counted_length_ft and overlap_stall_ids (see Adding up linear feet). source_polygons[] holds the painted stripe outline each line came from, with the same id.
Per-item ids. Every item has an id of the form <category>-<index>, e.g. stalls-12. Ids are unique within one response and match across counters, the per-polygon breakdowns, and GeoJSON features. A new detection of the same site can number items differently.
values#
Each category's values object holds every quantity that applies to it:
| Key | Unit | Categories |
|---|---|---|
count |
items | All |
area |
your unit |
crosswalks, crosshatches, stop_line; arrows and abc (count × 12 sq ft or 16 sq ft per item; measured_sqft adds the detected area when known) |
linear_feet |
LF |
Striping (sum of each stripe's counted_length_ft); stalls (stall striping); area markings (estimated paint) |
line_count |
lines | stalls, handicap, trucks (divider lines) |
units |
your label | Only when your workspace defines a custom unit rule for the category |
Each value is {"value": ..., "unit": ..., "formatted": ...}. Some include a breakdown:
- Stall linear feet = divider lines between stalls (18 ft each, 21 ft for truck stalls) plus the center line between facing rows.
breakdownhasrow_divider_lf,divider_line_count,center_spine_lf,center_spine_count,row_countanddivider_length_buckets. A divider between two different stall types (e.g. a regular stall next to a handicap stall) is counted once, on the handicap or truck category, inshared_divider_count/shared_divider_lf. Stall width defaults to 9 ft (handicap and truck 12 ft); your workspace settings can change it. - Crosswalk linear feet = border lines plus bars every 4 ft, by pattern. Hatching linear feet = outline plus diagonal stripes every 2 ft. Stop bar linear feet = bar length × bar count.
breakdown.polygons[]has one row per item, with the item'sidandlf. - Striping linear feet = the stripes' centerlines, less any stretch that lies on stall striping (counted under the stalls). See Adding up linear feet.
Adding up linear feet#
Every category's values.linear_feet.value adds to the others: stalls, handicap, trucks, crosshatches, crosswalks, stop_line, lines_white, lines_yellow and lines_double_yellow never count the same paint twice. Total striping paint is their sum.
Stall linear feet come from the stalls themselves (the dividers between stalls plus the center line between facing rows), so that paint is often detected as a stripe too. A stripe, or the stretch of one, that runs along a divider or center line counted in stall linear feet is counted under the stall category and left out of striping:
- A stretch is on the stall striping when it is within 1.5 ft of the divider or center line and within 20° of parallel to it.
- Only the dividers and center lines that stall linear feet count are matched. A row's end lines and the line along the stall fronts are not in stall linear feet, so they stay in striping.
- A long stripe that crosses many dividers, such as one running through a row, loses only the stretches that run along one.
Each stripe in counters.<category>.lines[] shows its split:
| Field | Description |
|---|---|
length_ft |
The detected length of the stripe. |
overlap_with_stalls_ft |
The length that runs along counted stall striping and is counted under the stalls. 0.0 when none. |
counted_length_ft |
length_ft − overlap_with_stalls_ft: what the stripe adds to the striping total. |
overlap_stall_ids |
The ids of the stalls, handicap stalls and truck stalls whose divider or center line the stripe runs along. Empty when none. |
The category's values.linear_feet has value (the sum of counted_length_ft), detected_lf (the sum of length_ft) and overlap_with_stalls_lf (the length counted under the stalls). counter_summary, areas[] and striping_by_phase use value. In GeoJSON, each stripe feature carries the same three fields, and its lf is its counted_length_ft.
"lines_white": {
"lines": [
{"id": "lines_white-3", "points": [[30.400710, -97.72187], [30.400766, -97.72187]],
"length_ft": 20.4, "overlap_with_stalls_ft": 18.0, "counted_length_ft": 2.4,
"overlap_stall_ids": ["stalls-14", "stalls-15"]},
{"id": "lines_white-4", "points": [[30.40061, -97.72190], [30.40061, -97.72121]],
"length_ft": 218.7, "overlap_with_stalls_ft": 0.0, "counted_length_ft": 218.7,
"overlap_stall_ids": []}
],
"values": {
"linear_feet": {"value": 221.1, "unit": "LF", "formatted": "221 LF",
"detected_lf": 239.1, "overlap_with_stalls_lf": 18.0}
}
}
Here the first stripe lies on the divider between stalls-14 and stalls-15: 18 ft of it is that divider, already in stalls, and the 2.4 ft past the stall ends stays in lines_white.
counter_summary#
One entry per category, ready to store. It has the same quantities as counters without the geometry:
"crosswalks": {
"label": "Crosswalks",
"color": "#14b8a6",
"group": "area",
"data_type": "polygon",
"display_modes": ["area", "linear_feet", "count"],
"default_display": "area",
"count": 6,
"formatted": "1,285 SF",
"values": {
"count": {"value": 6, "formatted": "6"},
"area": {"value": 1284.6, "unit": "SF", "formatted": "1,285 SF"},
"linear_feet": {"value": 1046.2, "unit": "LF", "formatted": "1,046 LF"}
},
"assumptions": {"default_width_ft": 6},
"area_sqft": 1285,
"linear_feet": 1046,
"lf_assumptions": {"bar_spacing_ft": 4.0},
"lf_breakdown": {"polygon_count": 6, "border_lf_total": 512.4, "bar_lf_total": 533.8}
}
area_sqft, linear_feet and line_count here are rounded to whole numbers. Use values.*.value (one decimal) for estimating.
areas#
With named areas, each entry is:
| Field | Type | Description |
|---|---|---|
name |
string | The name you sent. |
area_sqft |
integer | Pavement inside the area, in square feet. Left out when include_parking is false. |
excluded_islands |
object | Present when the request has islands: count (islands that overlap this area) and area_sqft (the part of this area's polygon they take out). |
counter_summary |
object | Same shape as the top-level one, for this area's items only. |
Items in counters and parking_areas carry area and phase set to the area name. The areas add up to the top-level totals.
Optional layers#
carports, concrete and sidewalk each have area_sqft, count and polygons[] (coordinates, area_sqft). A layer's area_sqft is the sum of its polygons[].area_sqft, and count is the number of polygons. coordinates is a list of rings ([lat, lng]): the first is the outline, any others are holes, and a polygon's area_sqft leaves its holes out. Shapes are clipped to your polygon; shapes and cut pieces under about 8 sq ft are not returned or counted. Carports also have estimated_stalls in total and per polygon; stalls under carports are added to counters.stalls, and carport area is added to summary.total_area. Concrete and sidewalk are not added to the paved total.
The paved-area model finds drivable paved surface whatever it is made of, so parking_areas can include concrete paving. To get asphalt only, send asphalt_only: true: concrete (and sidewalk, when include_sidewalk is on) is cut out of parking_areas and summary.total_area, and asphalt_only.removed_sqft is the paved area taken out. Carports are already their own layer.
Add-on models#
Sent with include_curbs, include_wheel_stops, include_site_objects or include_landscape. Each adds its own section. Add-ons never change counters, counter_summary or summary.
Every section has status (complete or failed; site_objects can also be partial) and, unless complete, error. A failed section has no quantities.
| Section | Fields |
|---|---|
curbs |
count (lines), linear_feet (0.1 ft), follows_paved_edges (true when the lines trace the detected pavement's edges), lines[]: id (curbs-<n>), points ([lat, lng]), length_ft |
wheel_stops |
count, points[]: id (wheel_stops-<n>), lat, lng |
site_objects |
count, count_by_class (parallel_stall, speed_bump, bollard: label, count, zero included), points[]: id (site_objects-<n>), class, label, lat, lng, confidence |
landscape |
unit (SF, SY, SM or AC), total_area, total_area_sqft, area_by_class (per area type found: label, count, area in unit, area_sqft), count_by_class (per object type found: label, count), tree_count (deciduous + evergreen + palm), point_count, polygons[]: id (landscape_areas-<n>), class, label, rings, area_sqft; points[]: id (landscape_points-<n>), class, label, lat, lng, confidence |
Landscape area types: driveway, road, parking_area, gravel, hedge, lawn, sidewalk, mulch, sand, clay, deck, pond, trash. Landscape object types: deciduous, evergreen, palm, shrub, stump, fountain. Only types that were found are listed.
For areas requests, add-on items also carry area, the named area they fall in.
imagery_source#
| Value | Meaning |
|---|---|
{"type": "google"} |
Google satellite imagery (the default). No capture date is available. |
{"type": "nearmap", "survey_id": "...", "date": "..."} |
The Nearmap survey you passed. date is the nearmap_date you sent, or null. |
{"type": "uploaded_tiles", ...} |
Your /detect/tiles upload: coverage_bounds, tile_count, meters_per_pixel_at_center, edge_handling and the size of each tile. |
Marking details#
Sent with marking_details: true. It only adds fields. Existing counts and quantities don't change.
Per item in counters:
| Field | On | Values |
|---|---|---|
paint_color |
every item | white, yellow, blue, red, green, black, other, unknown |
paint_color_source |
every item with paint_color |
Where the color comes from: checked (this item's own image was read), row (stalls and trucks: the checked stalls in its row), nearby (stalls and trucks: the nearest checked stall within 60 ft), line_class (lines: the white or yellow line type from detection, because the line wasn't read or its read didn't confirm a color), unknown |
arrow_type |
arrows |
straight, left, right, straight_left, straight_right, left_right, u_turn, merge, bike, other, unknown |
stencil_kind |
abc |
text, number, symbol, logo, wheelchair, other, unknown |
text |
abc |
The painted words, uppercase, or null when unreadable |
is_custom |
abc |
true for a logo or custom stencil, false for a stock stencil, null when unknown |
handicap_stall_id |
abc wheelchair symbols |
The id of the handicap stall the symbol is painted in, or null when it is outside every handicap stall |
wheelchair_symbol |
handicap |
true, false, or null when hidden. true also when a wheelchair symbol in abc is painted in the stall. |
wheelchair_symbol_ids |
handicap |
The abc wheelchair symbols painted in this stall, e.g. ["abc-12"]. Only present when there is one. |
divider_style |
stalls, trucks, handicap |
single (one painted line between neighboring stalls), double (two parallel painted lines about 1 to 2 ft apart, often joined at the ends), unknown |
divider_style_source |
stalls, trucks, handicap |
checked, row, nearby (the nearest checked stall within 60 ft) or unknown |
Paint color and divider style are read on a sample of stalls in every row; the other stalls in the row take the row's values (row), or the nearest checked stall's when the row has none (nearby). values.count.by_color_source on stalls and trucks counts the stalls by source, e.g. {"checked": 60, "row": 490, "nearby": 10, "unknown": 6}.
Also added: counters.abc.stencils[] (shapes grouped into painted messages), values.count.by_color and values.linear_feet.by_color in counter_summary, a top-level striping_by_color, and counter_summary.ada_stencils.values.detected_count. Each named area's counter_summary gets the same summary fields for its own items.
The top-level marking_details block reports status (complete, partial, failed, skipped or off), items_reviewed, items_unknown and notes. Marking details never fail a detection. On a synchronous request they are skipped if the detection used most of the available time; send async: true for large sites.
Stencils#
Each entry in counters.abc.stencils[] is one painted message. Its kind is in both kind and stencil_kind (the same value, so one name works for stencils and abc items). Wheelchair symbols are not stencils; they are counted under ada_stencils. values.stencil_count on abc counts the stencils, with by_color:
"stencil_count": {"value": 3, "formatted": "3", "by_color": {"white": 2, "yellow": 1}}
Wheelchair symbols#
ada_stencils.values.detected_count counts each painted wheelchair symbol once, whether it was seen in the handicap stall's own image, as an abc symbol, or both. Every handicap stall is in exactly one of in_handicap_stalls, handicap_stalls_without_symbol and handicap_stalls_unknown, and value is in_handicap_stalls + outside_handicap_stalls:
"detected_count": {
"value": 5, "formatted": "5", "basis": "detected",
"in_handicap_stalls": 4,
"outside_handicap_stalls": 1,
"handicap_stalls_without_symbol": 1,
"handicap_stalls_unknown": 1
}
An abc wheelchair symbol keeps duplicate_of: "ada_stencils" and names its stall in handicap_stall_id; that stall lists it in wheelchair_symbol_ids.
Stall striping#
Stall linear_feet.value counts one line for each divider between neighboring stalls, plus the center line between facing rows. It doesn't count the line at each end of a row, and it doesn't change with marking details. painted_lf is what is painted as far as the imagery shows: each double-line divider counts as two lines, each painted row end counts (two lines on a double-line row), and the center line counts once. Unknown divider styles count as one line; unknown row ends aren't included, so add them yourself if you want them. The short caps joining the two lines of a double-line divider (about 1 ft each) aren't included. When a double divider style in your measurement settings already doubles value, painted_lf starts from one line per divider and one center line, so nothing is counted twice.
values.linear_feet on stalls, trucks and handicap adds:
| Field | Meaning |
|---|---|
divider_styles |
Dividers between neighboring stalls by the style of their row: {"single": n, "double": n, "unknown": n}. A row of N stalls has N - 1. |
row_end_lines |
The two ends of every row: painted, not_painted, unknown and painted_lf (the painted ends' feet, two lines on a double-line row). An end next to another stall is a divider and isn't listed. Ends over the per-request limit are unknown. |
painted_lf |
Painted stall striping in feet, as above. |
"linear_feet": {
"value": 1314.0, "unit": "LF", "formatted": "1,314 LF",
"breakdown": {"divider_line_count": 70, "divider_line_size_ft": 18, "center_spine_lf": 54.0},
"divider_styles": {"single": 0, "double": 70, "unknown": 0},
"row_end_lines": {"painted": 10, "not_painted": 1, "unknown": 1, "painted_lf": 360.0},
"painted_lf": 2934.0
}
Here 1,314 LF = 70 dividers × 18 ft + 54 ft of center line, and 2,934 LF = 1,314 + 70 × 18 ft for the second line of each divider + 360 ft for 10 painted double-line row ends.
Other output formats#
format: "geojson"returns aFeatureCollectionwith[longitude, latitude]coordinates. Paved areas, layers and every item are features.properties.featureTypeisparking_area,carport,concrete,sidewalkorcounter; counter features also havecategory,label,idand their measurements. Add-on items are features withfeatureTypecurb,wheel_stop,site_object,landscape_areaorlandscape_point. The collection'spropertiesholdssummary, each add-on section without its geometry, andexcluded_islandswhen you sent islands.format: "kmz"returns a KMZ file (application/vnd.google-earth.kmz) with folders for paved areas, layers and counted items. Synchronous only.
Categories#
List categories#
GET /v1/categories
Permission: Any valid key
Returns metadata for every category: label, color, group, data type, the measurement types it supports and its defaults. Build your UI from this instead of hard-coding categories.
curl https://trutec.ai/api/v1/categories -H "X-API-Key: sk_your_api_key_here"
{
"categories": {
"stalls": {
"label": "Stalls",
"color": "#3b82f6",
"group": "stalls",
"data_type": "marker",
"display_modes": ["count", "line_count", "linear_feet"],
"measurement_types": ["count", "line_count", "linear_feet"],
"default_display": "count",
"default_width_ft": 9,
"default_sqft": 162
}
},
"groups": {
"stalls": {"label": "Stalls & Markings", "description": "Point-based detections"},
"area": {"label": "Area Markings", "description": "Polygon-based area detections"},
"lines": {"label": "Striping", "description": "Line-based striping detections"}
}
}
Detection returns these categories:
| Key | Label in results | Type | Measurement types |
|---|---|---|---|
stalls |
Stalls | marker | count, line count, linear feet |
handicap |
Handicap | marker | count, line count, linear feet |
trucks |
Trucks | marker | count, line count, linear feet |
arrows |
Arrows | marker | count, area |
abc |
Symbols | marker | count, area |
crosswalks |
Crosswalks | polygon | area, linear feet, count |
crosshatches |
Crosshatches | polygon | area, linear feet, count |
stop_line |
Stop Line | polygon | area, linear feet, count |
lines_white |
White Lines | line | linear feet, count |
lines_yellow |
Yellow Lines | line | linear feet, count |
lines_double_yellow |
Double Yellow | line | linear feet, count |
/categories also lists categories used in saved takeoffs (ada_stencils, ada_crosshatch, stall_centerline, lane_lines_skip, misc_lines). Its labels are the defaults; results use your workspace's labels or any counter_labels you send.
Units#
unit |
Area label | Linear |
|---|---|---|
sf (default) |
SF square feet |
LF |
sy |
SY square yards |
LF |
sm |
SM square meters |
LF |
acres |
AC acres |
LF |
Fields ending in _sqft are always square feet. Linear quantities are always feet.
Filtering#
Results match what the TruTec editor shows:
- Paved areas under 200 sq ft and holes under 50 sq ft are dropped.
- Hatching under 5 sq ft is dropped.
- When paved area is measured, markings outside it are dropped.
- Crosswalks take precedence over hatching, and hatching over stripes and stalls, so nothing is counted twice.