-
-
Brief: a contract claims Currimao; its pin lands in San Jacinto, 207 km away.
-
Evidence card: claimed town vs recorded pin, next step, and a benign explanation.
-
Inspection queue: 1,773 flagged contracts ranked beside a Philippines map.
-
Live label counts across all 9,855 contracts, plus featured mismatches to open.
-
Satellite view of the recorded pin so desk review can precede a site visit.
-
Method: every rule and threshold listed. Nothing scored by a black box.
Inspiration
A flood-control contract says Currimao. Its map pin lands in San Jacinto. Two hundred seven kilometers apart.
That is not a mystery novel. It is a row in a public spreadsheet. The Philippines published 9,855 flood control contracts worth about ₱547.6B. Verification is the bottleneck: as of late September 2026, DPWH has confirmed 10 ghost projects and nearly 200 open investigations. A pin that shows nothing is not automatically a ghost. It may be misplaced. Existing dashboards flag data. They do not sort "fix the record" from "send someone to the site."
We built GroundTruth for that gap: a triage queue that checks every published contract against the map, and never pretends a flag is a verdict.
Who it is for. Investigative journalists, watchdog researchers, and government validators (DPWH, COA, NBI, and similar desks) who need a defensible inspection queue, not a guilt machine. Citizens can also open their own town in the queue.
What it does
GroundTruth reads the full published set and hands investigators a ranked inspection queue.
- Resolve two claims. Each record names a municipality and often a place in the title. Both are matched to municipal boundaries. The claim closest to the pin drives the call.
- Run named checks. Deterministic rule codes (
OUTSIDE_CLAIMED_TOWN,SHARED_PIN,FAR_FROM_NAMED_WATERWAY, and others) fire with measured values, not vibes. - Label every row. Records issue likely · Field priority · Low priority · Insufficient data.
- Rank within each label. A 0–100 priority score. Money raises rank; it never creates a flag alone.
- Show the evidence. Locator diagram, claimed outline, related contracts, and a benign explanation next to every signal.
- Respect capacity. Keep only the top N when inspectors are limited, then export CSV or share a deep link.
Live counts from the build: 636 records-issue rows, 1,137 field-priority rows, 7,909 low priority, 173 insufficient data. One of them is that Currimao / San Jacinto mismatch. Open it. See the kilometres.
What makes it different. Most public dashboards stop at "flag the row." GroundTruth separates desk work from field work, resolves two location claims so same-named towns are not treated as missing structures, and puts the measured value beside an innocent explanation on every card. A Method page lists every threshold and every limit. It is a triage tool, not a verdict engine.
How we built it
Pipeline (Python): pandas + geopandas + shapely for geometry; rapidfuzz for municipality name matching; pyosmium against an OpenStreetMap Philippines extract for named waterways. Output is static JSON the browser can load without a backend to fail during judging.
Web (Vite + React + TypeScript): MapLibre GL on a greyscale Esri light-gray basemap (satellite toggle via Esri imagery), Motion for editorial transitions, virtualized queue for 9,855 rows. Museum-white / ink-black brand: Syne wordmark, Source Serif 4 headlines, DM Sans UI, IBM Plex Mono for IDs and scores.
Data: DPWH flood contracts via Sumbong sa Pangulo (public mirror), geoBoundaries ADM3 municipalities, OpenStreetMap waterways. Snapshot date is stated in the product.
Method page: every threshold, every limit, every credit. If a check cannot prove a structure exists, the copy says so.
Built during LovHack Season 3
All GroundTruth product code for this submission was created between September 26 and October 4, 2026: the Python triage pipeline, scoring and label logic, the React/MapLibre web app (Brief, Queue, evidence cards, Method), capacity slider, CSV export, deep links, and the demo video.
What existed before the hackathon: public datasets only (DPWH / Sumbong sa Pangulo mirror, geoBoundaries, OpenStreetMap) and open-source libraries. No prior GroundTruth application, UI, scored queue, or marketing video.
Sponsor challenges: we are not entering a sponsor-specific prize. GroundTruth does not integrate deAPI or other sponsor SDKs.
Challenges we ran into
Same town, wrong province. The largest raw kilometre mismatches were often encoding errors: a municipality field that picked a same-named town elsewhere while the title and pin agreed. Treating that as a "ghost" would have been reckless. Two-claim resolution fixed it.
Signal vs noise. Shared pins and consecutive contract numbers look damning until you remember phased works and lot-splitting. Every flag needed an innocent explanation on the card.
No backend, still fast. Shipping 9,855 scored rows as static JSON meant careful payload design and a virtualized list so the queue feels instant.
Ethics under a deadline. Neutral language only. No contractor rankings. No "ghost" in product UI copy. Restraint took more design time than the map did.
Accomplishments that we're proud of
- A live product on all 9,855 published contracts, not a curated demo subset.
- Labels that separate desk work from field work: 636 records issues vs 1,137 field priorities.
- Evidence cards that show the measured value and the benign alternative side by side.
- A Method page that states what the tool cannot do.
- A cold-open story you can verify yourself: open Currimao, watch the pin land 207 km away.
What we learned
Public scandal data is not the same as an inspection queue. Flagging is easy. Sorting what to fix, what to visit, and what to leave alone is the product. Geography without humility lies; every mismatch we trusted only after we could name a boring explanation for it.
What's next for GroundTruth
Fresh snapshots when the portal updates. Barangay-level boundaries where they exist. Optional imagery overlays for field teams. Deeper consecutive-run clustering. Still never a verdict engine: only a better first pass before boots hit the ground.
Check the record. Then check the ground.
Built With
- geoboundaries
- geopandas
- maplibre-gl
- motion
- openfreemap
- openstreetmap
- pandas
- pyosmium
- python
- rapidfuzz
- react
- shapely
- typescript
- vercel
- vite


Log in or sign up for Devpost to join the conversation.