Occurrences Flow & GIS Automation

Designing the Flow

Building a system to handle emergency response and field logistics is always a high-stakes puzzle. In this deep dive, I want to show how we structured and optimized the Occurrences module of Saffira, our platform for real-time dispatch and disaster management.

The occurrence registration system already existed when I joined Aton. However, the experience was raw and sluggish. Operators were forced to navigate a single, massive form to log details while answering high-stress emergency calls.

My task was to overhaul the frontend data management and apply a brand-new UI/UX. I focused on breaking down that massive entry flow, structuring it into a dedicated occurrences menu where information is split and categorized. This allowed operators to fill in coordinates, affected resources, and team assignments at their own pace.

High-Level Architecture

We designed an event-driven system that bridges web applications, physical hardware gateways, and spatial database engines. Below is how the Angular client, Express backend, MongoDB cluster, and external systems (like Geoserver and ONVIF camera controllers) interface with each other.

Saffira Occurrences High-Level System Architecture map

Figure 1.1: Saffira Occurrences High-Level System Architecture map.

Occurrences Lifecycle Simulation
Client GIS InterfaceSpatial Query Server
Map Intersection
Click Coordinate[lng, lat] capture
GeolocationHelper
Normalizes column keysWFS spatial payload
Geoserver WFS
Geometry intersectresolves map shapes
Tenant Translation
TALHAO ➔ talhaomaps Suzano/Bracell/UISA
UI Autofill
municipalityfarm & block

Figure 1.2: Interactive occurrence registration and automation flow simulation.

Technical Details

1. GIS Query & Spatial Logic

One of the biggest hurdles was discovering how client GIS departments structured their map layers. When auditing Suzano, Bracell, and UISA, we noticed they all named their map database columns differently. For example, a farm block was TALHAO for one client, UP for another, and Talhao for a third.

This mapping is necessary because we query WFS (Web Feature Service) spatial metadata at the clicked coordinate to link geographic shape data with the newly created occurrence. Since each client enforces their own unique nomenclature for map shapes, this translation layer standardizes the shape properties on the fly.

Our helper service, GeolocationHelperService, acts as this translation bridge:

Target PropertyBracell FieldSuzano FieldUISA Field
nomePropriedadePROJETOFazenda / fazendaNome_Faz
talhaoTALHAOUPTalhao
zonaID_PROJETONUCLEOZona
areaEstimadaAREA_HAAreaRotaca / AreaÁrea Tota

2. Incident Creation & PTZ Automation

When an operator saves an occurrence, the frontend triggers a POST request. Instead of forcing the operator to wait for camera hardware and message dispatches to execute, the controller responds immediately (201 Created) and hands the background tasks to a worker job.

The background worker query checks for active ONVIF PTZ cameras within range, calculates the necessary pan/tilt/zoom angles, dispatches the camera motors, waits 4.5 seconds for hardware autofocus, takes snapshot frames, uploads them to MinIO, and alerts managers via Telegram with the snapshot attachment.

3. Dynamic Multi-Tenancy

We upgraded Saffira's backend multi-tenant architecture by introducing a new schema structure compiled dynamically at runtime, reducing connection pool exhaustion and code bloat:

private get OccurrenceModel() { const client = process.env.CLIENT || "Dev"; const modelName = `${client}Model`; const resolvedModel = Reflect.get(modelOcorrencias, modelName); if (!resolvedModel) { throw new Error(`Model not found for client: ${client}`); } return resolvedModel; }

4. Database Design: Thread-Safe Atomic Writes

To avoid database join overhead during critical incident response operations, we design a single consolidated Mongoose document schema. All related records crew, vehicles, telemetry, weather, and camera snaps are embedded inside nested document arrays.

OcorrenciaSchema Definition
_idTypes.ObjectId
registroSubDocument

Core metadata tracking the occurrence status, title, and initial dispatcher notes.

Embedded Fields
idOcorrenciaString
Unique human-readable identifier
tituloString
Brief title describing the fire
statusBoolean
false = active alert, true = resolved (debelado)
descricaoString
Dispatcher notes and field descriptions
dataDate
Incident registration timestamp

Because multiple dispatchers are active on the same incident, standard document saves would cause write race conditions. We bypass this by executing native atomic MongoDB operations ($push, $set, $pull):

// Positional Sub-Resource Updates async updateLocal(occurrenceId: string, localId: string, updateData: any) { return await this.OccurrenceModel.findOneAndUpdate( { _id: occurrenceId, "locaisAfetados._id": new Types.ObjectId(localId) }, { $set: { "locaisAfetados.$": updateData } }, { new: true } ); }

5. Operational Cost Calculations Engine

Calculating combat expenses in real-time is a core operational requirement. We write custom analytical helpers that sum vehicle, personnel, and fuel variables into brigade aggregates:

Fuel Cost = Fuel Liter Price × KM Traveled × N
Lance Cost = Base Lance Price × Lance Quantity × N
Total Personnel Cost = Crew Quantity × Unit Rate

6. Signals Caching & Cross-Tab Replication

To protect our servers from repeated requests during emergency events, the Angular client uses a memory caching dictionary. On write operations, we invalidate the cache.

To keep multiple browser tabs aligned without making backend database queries, we route events locally using the browser's native BroadcastChannel API.

Cross-Tab Replicator SimulationSimulate how separate browser tabs sync state using the native BroadcastChannel API.
Tab A: Map Monitor
OpenLayers Canvas
Active layers: 2
Tab B: Incident List
TitleCoordinatesAgg. Cost
Foco Inicial - Talhão 04[-22.312, -49.011]R$ 4.250
Incêndio Ativo - Faz. Santa Rita[-22.325, -48.985]R$ 12.800
● Standby Link

Wrap up

Overhauling the occurrences flow wasn't just about speed; it was about the operator's state of mind. Standing in the shoes of a dispatcher answering high-stress emergency calls, I realized that every second spent navigating a convoluted form or waiting for physical camera feeds to load was a second of critical delay. Offloading heavy camera panning, MinIO uploads, and alert dispatches to detached background threads transformed a sluggish 45-second waiting cycle into an immediate, reassuring screen lock.

By bridging frontend Signals, BroadcastChannel sync, and atomic MongoDB operations, we got rid of the developer's constant worry over data drift and connection limits. More importantly, we built operational trust. Automated real-time billing calculations replaced error-prone spreadsheets, and immediate Telegram notifications with attached camera snaps kept the entire field team in lockstep. Ultimately, the biggest win wasn't just a 99.8% cache hit rate; it was giving emergency operators the quiet focus they needed to coordinate when every second mattered.