
Central de Inteligência (TerraLogs)
A geospatial intelligence platform where the hard engineering is entirely in the browser — an object-oriented layer engine over Google Maps.
TerraLogs turns large geospatial datasets into an interactive map: rural properties, territories, environmental areas and mining processes, drilled from national scope down to a single município. This is a front-end-only role, which usually means the interesting problems live elsewhere — here they did not. Rendering thousands of features across five rendering strategies, keeping them consistent while filters change faster than requests resolve, and doing it without freezing the tab, needed a real architecture. I own that engine; a second engineer works alongside me on the product.
ROLE
Front-End Engineer
Map Architecture
TOOLS
Vue 3
Quasar
TypeScript
TanStack Query
Google Maps API
Supercluster
Turf.js
ECharts
DURATION
2025 - Present
INTEGRATIONS
Google Maps
AWS Cognito (OIDC)
OpenAPI REST backend
✶ An OO layer engine, not a pile of map callbacks
4.4k lines of pure TypeScript under src/core, with no Vue in it. Layers are declared as data — including their click handlers, as typed string keys into a registry — and a factory picks one of five implementations behind a single ILayerType contract. Adding a rendering strategy is a new subclass and one switch case.
✶ GeoJSON: eight stages between the API and a click
The pipeline exists because the naive version fails quietly. Google's Data layer fires no events on a GeometryCollection, so every one is expanded into standalone features that keep the parent's id and properties — otherwise clicks simply stop working, with no error anywhere to point at.
Raw response → clickable map layer
raw response
three shapes from the same API
feature vs features · features:[[…]] double-nested · paginated
normalize + accumulate
page 1 reveals totalPages, the rest fetched in parallel
callers never learn the endpoint was paginated
Object.freeze(geojson)
before it ever reaches reactive state
stops Vue building deep proxies over thousands of features
expand GeometryCollection
each inner geometry becomes its own Feature, keeping parent id + properties
google.maps.Data fires no events on a GeometryCollection — clicks would die silently
local filters
supply chain · titular · fase do processo
accent-folded, and digs through three possible property shapes
dejitter collocated points
bucket by 6-decimal key, spiral the rest onto 8-slot rings at ~9 m
stable across reloads by id sort — pins stop hiding under each other
style closure per feature
selection highlight · point vs polygon · opportunity icon swap
updateSelectedId() repaints without re-adding a single feature
google.maps.Data
hit-testing gated to Point / MultiPoint on territory layers
otherwise the polygon fill swallows every pin click inside it
✶ Filters change faster than requests resolve
Changing município mid-flight aborts the old request, but aborting is not enough — the response can still arrive. Every payload is revalidated against the current filters after the await and discarded if the scope moved, with the same guard mirrored on the error path so a stale 404 never surfaces over data you are actually looking at.
Why the map never paints a filter you already left
user
picks município A
UrlResolver produces url A · fetch starts
user
changes to município B
300 ms later — the A request is still in flight
code
url changed → abort
and resetLayers() runs immediately, before the async abort handler
code
fetch A resolves anyway
abort is not instantaneous — the response still arrives
code
re-resolve url against current filters
A ≠ B → discard the payload, never paint it
code
same guard on the error path
a stale 404 for A never raises a toast while you are looking at B
code
cleanup only if still current
the finally block compares controller identity before clearing the map entry
✶ Selection rules as a pure function
Five interacting rules — single, special, drill-down, exclusive, default — resolved in fixed precedence by toggle(id, activeSet) → Set. No map, no network, no framework, including the inverted case where deactivating a parent activates its children. Pure by design, so the behaviour stays testable instead of scattering across components.
✶ Keeping the main thread alive
GeoJSON is frozen before it reaches reactive state so Vue never builds deep proxies over thousands of features; every Google object is markRaw'd; clustering culls to the viewport and re-renders only on idle; and switching visualization modes yields to the browser between teardown steps rather than blocking straight through them.
✶ Two domains, one engine
The same core drives AGRO — verified and estimated properties, irrigation pivots, indigenous and quilombola territories, protected areas — and MineraLogs, over ANM mining processes with phase, holder and opportunity ranking. They differ by configuration, not by code.