Aplicativos Análise Telemática e GIS

Gerador de Ferramenta Forense para Análise de ERB e CDR

Cria uma aplicação web interativa para mapear deslocamentos e setores de antenas a partir de dados telemáticos de operadoras em investigações criminais.

☆☆☆☆☆
avaliação
0.0/5
nota média
33
visualizações totais
0
avaliações
prompt completo
# PROMPT DE ENGENHARIA DE SOFTWARE FORENSE ## Gerador da Ferramenta Interativa de Visualização Telemática (ERB / CDR) **v2.0 — Especificação Completa** --- > **INSTRUÇÕES DE USO** > > Copie e cole **TODO** este prompt em um LLM (Claude, GPT-4, etc). > > A ferramenta será entregue como **arquivo HTML único autossuficiente** que funciona offline. --- ## 1. PAPEL E CONTEXTO Você é um **Engenheiro de Software Sênior** especializado em: - Sistemas de Informações Geográficas (GIS) com Leaflet.js - Ferramentas Forenses de Análise Telemática para investigações policiais - Design tático de interfaces para órgãos de segurança pública - Processamento e validação de dados telemétricos **Sua tarefa:** Criar uma aplicação web **interativa em arquivo ÚNICO HTML** (HTML5 + CSS Tailwind + JavaScript vanilla + Leaflet.js + SheetJS) que processe planilhas Excel (.xls, .xlsx) e CSV de extratos de Estação Rádio-Base (ERB) e Call Detail Records (CDR), visualizando: 1. **Histórico cronológico de deslocamento** do alvo (timeline interativa) 2. **Setores angulares de cobertura** (polígonos de 120°) de cada ERB 3. **Azimute central** (linha de orientação da antena) 4. **Metadados forenses** (coordenadas de precisão, timestamps, identificadores) --- ## 2. REQUISITOS FUNCIONAIS ### 2.1 Leitura e Mapeamento Dinâmico de Colunas A ferramenta deve ler automaticamente planilhas e mapear colunas com **tolerância a variações** de nomenclatura entre operadoras brasileiras (VIVO, TIM, CLARO, OI). Use este schema para mapeamento: ```json { "COLUMN_MAPPING": { "latitude": [ "Latitude RS", "Latitude RE", "Latitude", "lat", "LAT", "LATITUDE", "Lat", "latitude" ], "longitude": [ "Longitude RS", "Longitude RE", "Longitude", "lon", "LON", "LONGITUDE", "Lon", "longitude" ], "azimuth": [ "Azimute RS", "Azimute RE", "Azimute", "AZIMUTE", "Azimuth", "azimuth", "Azi" ], "terminal": [ "Número A", "MSISDN", "Terminal", "Número", "TERMINAL", "Alvo", "CPF", "IMSI" ], "dateStart": [ "Data Início", "Data", "DataInicio", "Data Partida", "Start Date" ], "timeStart": [ "Hora Início", "Hora", "HoraInicio", "Hora Partida", "Start Time" ], "siteId": [ "SiteID RS", "SiteID", "Site ID", "Site", "SetorID RS", "SetorID", "ID_ERB" ], "municipality": [ "Município ERB RS", "Cidade", "Município", "Municipality", "Local" ], "address": [ "Endereço ERB RS", "Endereço", "Address", "Logradouro", "Localização" ] } } ``` **Regras de parsing:** | Campo | Formato Aceito | Tratamento | |-------|---|---| | **Latitude/Longitude** | Numérico com vírgula ou ponto | Converter `,` → `.`; validar range: lat ∈ [-90, 90], lon ∈ [-180, 180] | | **Azimute** | Inteiro 0-360 ou `Ausente` | Se ausente/zero/nulo: usar `0°` (norte); se >360: aplicar `mod 360` | | **Data** | `YYYYMMDD` ou `DD/MM/YYYY` | Tentar parser nessa ordem; suportar `-`, `/`, `.` como separadores | | **Hora** | `HHMMSS` ou `HH:MM:SS` | Combinar com data em DateTime ISO; validar range 00:00:00 – 23:59:59 | | **Colunas ausentes** | Fallback | Usar `0` para coords, `0°` para azimute, `"N/A"` para texto | --- ### 2.2 Lógica Matemática e Geometria Forense #### A. Setor Angular de 120° ("Fatia de Pizza") Para cada registro com coordenadas válidas: 1. **Abertura total: 120°** (Azimute − 60° até Azimute + 60°) 2. **Raio configurável dinamicamente** via slider na interface: - Faixa: 100m a 2.500m - Padrão: 500m - Atualização em tempo real ao mover slider 3. **Cálculo geodésico:** - Usar fórmula de destino com raio da Terra: R = 6.378.137m - Gerar 9+ vértices do arco (incremento de 15° cada) - Fechar polígono retornando ao centro **Pseudocódigo:** ```javascript function createSectorPolygon(centerLat, centerLon, azimuth, radiusMeters) { const R = 6378137; // metros const startAngle = (azimuth - 60) * (Math.PI / 180); const endAngle = (azimuth + 60) * (Math.PI / 180); const vertices = [[centerLat, centerLon]]; // incluir centro for (let angle = startAngle; angle <= endAngle; angle += (15 * Math.PI / 180)) { const destLat = Math.asin( Math.sin(centerLat * Math.PI/180) * Math.cos(radiusMeters / R) + Math.cos(centerLat * Math.PI/180) * Math.sin(radiusMeters / R) * Math.cos(angle) ) * (180 / Math.PI); const destLon = centerLon * Math.PI/180 + Math.atan2( Math.sin(angle) * Math.sin(radiusMeters / R) * Math.cos(centerLat * Math.PI/180), Math.cos(radiusMeters / R) - Math.sin(centerLat * Math.PI/180) * Math.sin(destLat * Math.PI/180) ); vertices.push([destLat, destLon * (180 / Math.PI)]); } vertices.push([centerLat, centerLon]); // fechar polígono return vertices; } ``` #### B. Linha de Azimute Central Desenhe uma linha estendida que: - Parte do centro da ERB - Segue na direção exata do azimute - Estende-se até o raio do setor - Cor: cyan (#0ea5e9), espessura 2px, opacidade 0.8 #### C. Ordenação Cronológica (OBRIGATÓRIO) **TODOS** os registros devem ser ordenados cronologicamente **crescente** (mais antigo → mais recente) **antes** de construir a timeline e renderizar o mapa. Se dois registros tiverem o mesmo timestamp: - Ordenar alfabeticamente por `siteId` - Se `siteId` também igual, manter ordem original (stable sort) --- ### 2.3 Estrutura de Dados: Mock Dataset Inicial A aplicação deve **abrir imediatamente funcionando** com dados de exemplo. Use este padrão: ```json { "mockDataset": { "terminal": "11987654321", "operadora": "VIVO", "municipios": ["São Paulo", "Guarulhos", "Osasco"], "records": [ { "index": 1, "latitude": -23.550520, "longitude": -46.633309, "azimuth": 45, "datetime": "2024-01-15T14:15:00", "siteId": "ERB-SP-001", "municipality": "São Paulo", "address": "Avenida Paulista, 1000" }, { "index": 2, "latitude": -23.528702, "longitude": -46.624891, "azimuth": 135, "datetime": "2024-01-15T15:02:30", "siteId": "ERB-SP-002", "municipality": "São Paulo", "address": "Rua Augusta, 2500" }, { "index": 3, "latitude": -23.437640, "longitude": -46.470670, "azimuth": 270, "datetime": "2024-01-15T16:45:15", "siteId": "ERB-GUA-001", "municipality": "Guarulhos", "address": "Avenida Monteiro Lobato, 500" }, { "index": 4, "latitude": -23.531090, "longitude": -46.779050, "azimuth": 0, "datetime": "2024-01-15T17:30:00", "siteId": "ERB-OSA-001", "municipality": "Osasco", "address": "Estrada de Ferro, 300" }, { "index": 5, "latitude": -23.550520, "longitude": -46.633309, "azimuth": 180, "datetime": "2024-01-15T18:15:45", "siteId": "ERB-SP-001", "municipality": "São Paulo", "address": "Avenida Paulista, 1000" }, { "index": 6, "latitude": -23.570400, "longitude": -46.656200, "azimuth": 90, "datetime": "2024-01-15T19:00:30", "siteId": "ERB-SP-003", "municipality": "São Paulo", "address": "Rua Oscar Freire, 800" }, { "index": 7, "latitude": -23.437640, "longitude": -46.470670, "azimuth": 315, "datetime": "2024-01-15T20:10:00", "siteId": "ERB-GUA-001", "municipality": "Guarulhos", "address": "Avenida Monteiro Lobato, 500" }, { "index": 8, "latitude": -23.528702, "longitude": -46.624891, "azimuth": 225, "datetime": "2024-01-15T21:30:15", "siteId": "ERB-SP-002", "municipality": "São Paulo", "address": "Rua Augusta, 2500" } ] } } ``` **Características do mock:** - ✓ 8 registros cronologicamente ordenados - ✓ Variação de azimutes (45°, 135°, 270°, 0°, 180°, 90°, 315°, 225°) - ✓ Múltiplos municípios (SP, Guarulhos, Osasco) - ✓ Repetição intencional de sites (ERB-SP-001 aparece 2x para testar) - ✓ Timestamps em saltos realistas (45min–1h20min entre eventos) --- ## 3. REQUISITOS VISUAIS ### 3.1 Paleta de Cores (Hex) ```json { "colors": { "bgPrimary": "#0f172a", // Fundo escuro principal (slate-950) "bgSecondary": "#1e293b", // Fundo secundário (slate-800) "bgTertiary": "#334155", // Fundo terciário (slate-700) "textPrimary": "#f1f5f9", // Texto principal (slate-100) "textSecondary": "#94a3b8", // Texto secundário (slate-400) "textTertiary": "#64748b", // Texto terciário (slate-500) "accentCyan": "#0ea5e9", // Azul tático (cyan-500) "accentPink": "#ec4899", // Rosa/Fúcsia ativa (pink-500) "sectorInactive": "rgba(225, 29, 72, 0.15)", // rose-600 + alpha 0.15 "sectorActive": "rgba(236, 72, 153, 0.8)", // pink-500 + alpha 0.8 "borderDefault": "#475569", // Borda (slate-600) "borderHighlight": "#0ea5e9", // Borda destaque (cyan) "success": "#10b981", // Verde (emerald-500) "warning": "#f59e0b", // Amarelo (amber-500) "danger": "#ef4444" // Vermelho (red-500) } } ``` ### 3.2 Layout Estrutural **Viewport total: 100vh × 100vw** Dividida em 3 seções: ``` ┌────────────────────────────────────────────────────┐ │ HEADER (h-16, ~80px) │ ├──────────────────────┬──────────────────────────────┤ │ │ │ │ MAPA LEAFLET │ TIMELINE & DETALHES │ │ (60% width) │ (40% width) │ │ w-[60%] │ w-[40%] │ │ h-[calc(100vh-80px-70px)] │ h-[calc(100vh-80px-70px)] │ │ │ │ │ │ │ PLAYER (bottom) │ │ │ h-[70px] │ │ └──────────────────────┴──────────────────────────────┘ ``` ### 3.3 Componentes da Interface #### A. HEADER (Altura: 64px) ```html
[#0f172a] border-b border-[#475569]">

POLÍCIA CIVIL — ANÁLISE TELEMÁTICA ERB/CDR

Alvo: {{terminal}} Eventos: {{totalRecords}} Setor: 120°
``` **Badges:** - **Alvo:** "11987654321" (ou valor do terminal) - **Eventos:** "8 registros" (total de linhas processadas) - **Setor:** "120°" (fixo, informativo) #### B. ÁREA ESQUERDA: Mapa Leaflet (60% width) ```html
[60%] h-[calc(100vh-80px-70px)] relative">
[#1e293b] p-2 rounded">
``` **Mapa:** - Tiles: CARTO Dark Matter (`https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png`) - Zoom inicial: enquadrar todos os pontos (fitBounds) - Controles padrão: zoom + layers - Pops-up interativos ao clicar em marcadores/polígonos #### C. ÁREA DIREITA: Timeline & Detalhes (40% width) Dividida em 3 sub-seções: **C1. Search Bar (h-12)** ```html
[#1e293b] p-2 border-b border-[#475569]"> [#334155] text-[#f1f5f9]" />
``` **C2. Timeline Cards (flex grow)** ```html
45°

ERB-SP-001

São Paulo

``` **C3. Painel de Detalhes (h-32, fixo no rodapé)** ```html
[#0f172a] border-t border-[#475569] p-4">

{{siteId}}

Latitude:{{lat.toFixed(6)}}
Longitude:{{lon.toFixed(6)}}
Azimute:{{azimuth}}°
Endereço:{{address}}
``` #### D. PLAYER DE TIMELINE (Altura: 70px, rodapé) ```html
[70px] bg-[#1e293b] border-t border-[#475569] p-3 flex items-center gap-4">
[#94a3b8] text-center"> Registro 1 / 8
``` --- ## 4. INTERATIVIDADE E SINCRONIZAÇÃO ### 4.1 Sincronização Bidirecional #### Click em Card da Timeline → Mapa Ao clicar em um card na área direita: 1. **Map Pan/Fly:** - `map.flyTo([lat, lon], 16)` com duração 300ms - Easing: quadratic 2. **Highlight do Setor:** - Remover highlight do setor anterior - Aplicar cor `rgba(236, 72, 153, 0.8)` ao novo setor - Desenhar borda grossa (3px) em cyan (#0ea5e9) - Abrir popup com detalhes 3. **Card Visual:** - Adicionar classe `.active` com fundo pink/rosa - Scroll automático: `cardElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })` #### Click em Polígono/Marcador no Mapa → Timeline Ao clicar em um setor ou marcador no mapa: 1. **Timeline Scroll:** - Encontrar card correspondente pelo índice de registro - `cardElement.scrollIntoView({ behavior: 'smooth', block: 'center' })` 2. **Card Highlight:** - Adicionar classe `.active` - Fade-in 200ms 3. **Painel de Detalhes:** - Atualizar dados do registro selecionado - Mostrar coordenadas com 6 casas decimais ### 4.2 Timeline Player (Play/Pause/Velocidade) ```javascript // Pseudocódigo currentIndex = 0; isPlaying = false; playSpeed = 1; // 1x, 2x, 5x function play() { isPlaying = true; const intervals = { 1: 500, 2: 250, 5: 100 }; // ms const interval = setInterval(() => { if (currentIndex < records.length - 1) { currentIndex++; updateMapAndTimeline(currentIndex); updateScrubber(); } else { // Atingiu fim: parar automaticamente (NÃO loopear) isPlaying = false; clearInterval(interval); } }, intervals[playSpeed]); } function pause() { isPlaying = false; clearInterval(interval); } function updateSpeed(newSpeed) { playSpeed = newSpeed; if (isPlaying) { pause(); play(); // reiniciar com nova velocidade } } ``` --- ## 5. TRATAMENTO DE ERROS E VALIDAÇÃO ### 5.1 Validação de Dados | Cenário | Ação | |---------|------| | Latitude/Longitude fora de range | Ignorar registro; logar no console: `"[WARN] Registro #5: coordenadas inválidas"` | | Data malformada | Tentar parsers: `YYYYMMDD` → `DD/MM/YYYY` → `ISO8601`; se falhar, usar timestamp "N/A" | | Azimute ausente/0/nulo | Usar `0°` (norte) | | Azimute > 360 | Aplicar `mod 360` | | Hora inválida (ex: 25:00:00) | Usar `00:00:00` | | Coluna não encontrada | Usar valor default: `lat/lon = 0`, `azimuth = 0°`, `text = "N/A"` | | Arquivo vazio | Exibir mensagem: `"❌ Nenhum registro válido encontrado no arquivo"` | | Arquivo corrompido | Exibir: `"❌ Erro ao ler arquivo. Verifique formato .xlsx ou .csv"` | ### 5.2 Exibição de Erros (Sem `alert()`) Usar container visual fixo no topo da interface: ```html ``` **Tipos de mensagens:** - **Sucesso:** Verde (#10b981), 3 segundos, depois fade out - **Aviso:** Amarelo (#f59e0b), 5 segundos - **Erro:** Vermelho (#ef4444), 8 segundos (ou até clicar X) --- ## 6. PERFORMANCE E ESCALABILIDADE ### 6.1 Limite e Otimizações | Limite | Comportamento | |--------|---| | < 500 registros | Renderizar todos os setores normalmente | | 500–1.000 registros | Avisar: `"⚠️ Grande volume de dados. Aplicação pode ficar lenta; considere filtrar por data."` | | > 1.000 registros | Rejreitar com mensagem; sugerir split em múltiplos uploads | ### 6.2 Timeline Virtualização Para > 100 cards na timeline: - Renderizar apenas cards **visíveis** na viewport - Manter scroll suave (uso de intersection observer) - Cache de templates HTML ### 6.3 Busca em Tempo Real - Debounce: 300ms - Filtrar por: `terminal`, `siteId`, `municipality`, `data` (substring match case-insensitive) - Destacar matches em amarelo (#f59e0b) --- ## 7. RESPONSIVIDADE ### 7.1 Breakpoints | Breakpoint | Layout | Comportamento | |---|---|---| | **Desktop** (≥1024px) | 60% mapa \| 40% timeline | Normal | | **Tablet** (768–1023px) | 50% mapa \| 50% timeline | Ajustar tamanhos proporcionais | | **Mobile** (<768px) | Stack vertical (100%) | Mapa acima, timeline abaixo com height máxima (40vh) | ### 7.2 Mobile-Specific - Desabilitar renderização de setores de 120° (trocar por marcadores simples com circulo de raio) - Ocultar painel lateral, mostrar em tab/modal - Player em posição fixed bottom - Busca com teclado numérico otimizado --- ## 8. POPUP DO MAPA Ao clicar em marcador ou polígono, exibir popup com: ```html ``` **Ação ao copiar:** Copiar para clipboard no formato `LAT, LON` (ex: `-23.550520, -46.633309`); exibir toast "✓ Coordenadas copiadas". --- ## 9. ANIMAÇÃO E TIMING ### 9.1 Transições | Elemento | Propriedade | Duração | Easing | |---|---|---|---| | Mapa flyTo | Pan/zoom | 300ms | quadratic | | Highlight setor | Opacity + border | 200ms | ease-out | | Card fade-in | Opacity | 200ms | ease-in | | Tooltip | Opacity | 100ms | ease-out | | Error notification | Fade out | 300ms | ease-out | ### 9.2 Player Timing - **Velocidade 1x:** incrementa índice a cada **500ms** - **Velocidade 2x:** incrementa índice a cada **250ms** - **Velocidade 5x:** incrementa índice a cada **100ms** ### 9.3 Comportamento ao Fim Ao atingir o **último registro:** - Parar automaticamente (NOT loop) - Manter último evento destacado - Resetar botão Play para estado inicial --- ## 10. RESTRIÇÕES TÉCNICAS E ENTREGA ### 10.1 Arquivo Único HTML **Requisito:** Arquivo `.html` autossuficiente, executável offline. **Imports via CDN:** ```html ``` ### 10.2 Restrições JavaScript - ❌ **NUNCA** usar `alert()`, `confirm()`, `prompt()` - ✅ Usar containers visuais para mensagens (notificações) - ✅ Eventos de erro logar no console (`console.warn`, `console.error`) - ✅ Código limpo sem placeholders (`...`), omissões ou comentários TODO ### 10.3 Estrutura de Código ```javascript // Estrutura esperada const APP = { config: { /* paleta, limites */ }, data: { records: [], currentIndex: 0 }, ui: { map, timeline, controls }, handlers: { onLoadFile, onClickCard, onPlay }, utils: { parseDate, validateCoords, formatDMS } }; // Inicializar document.addEventListener('DOMContentLoaded', () => { APP.init(); APP.loadMockData(); APP.render(); }); ``` --- ## 11. CRITÉRIOS DE ACEIÇÃO A ferramenta será considerada **pronta** quando: - ✅ Página abre imediatamente com mock dataset visível - ✅ Mapa Leaflet carrega com tiles CARTO Dark Matter - ✅ Todos os 8 registros mock renderizam como polígonos + marcadores - ✅ Setores de 120° renderizam corretamente com geometria geodésica - ✅ Linha de azimute central desenha-se em cyan (#0ea5e9) - ✅ Timeline ordena cronologicamente (evento 1 → 8) - ✅ Clicking card na timeline → mapa flyTo + highlight + popup - ✅ Clicking polígono no mapa → timeline scrolla + card destaca - ✅ Slider de raio (100–2500m) atualiza setores em tempo real - ✅ Player: Play/Pause/Anterior/Próximo funcionam sincronizados - ✅ Seletor velocidade (1x/2x/5x) altera timing do play - ✅ Busca filtra cards dinamicamente (debounce 300ms) - ✅ Botão "Carregar Planilha" dispara file picker - ✅ Upload .xlsx/.csv valida colunas, mapeia dinamicamente, renderiza novo dataset - ✅ Tratamento de erros: sem `alert()`, mensagens em container visual - ✅ Coordenadas exibidas com 6 casas decimais no painel de detalhes - ✅ Popup mostra todas as informações do registro - ✅ Botão "Copiar Coordenadas" copia `LAT, LON` para clipboard - ✅ Botão "Enquadrar Tudo" (fitBounds) centraliza mapa em todos os pontos - ✅ Paleta de cores segue hex specification (escuro, cyan, pink) - ✅ Responsividade: desktop (60/40) e mobile (stack vertical) - ✅ Nenhuma função `alert()`, `confirm()` ou `prompt()` - ✅ Código limpo, sem placeholders, sem omissões --- ## 12. EXEMPLO DE SAÍDA ESPERADA Ao carregar a página, o usuário verá: 1. **Header** com logo, título, badges (Terminal: 11987654321, Eventos: 8, Setor: 120°) 2. **Mapa** (60% left) com: - 8 marcadores numerados - 8 polígonos de setor 120° em rosa/fúcsia semitransparente - 8 linhas de azimute central em cyan - Zoom enquadrando toda a região SP–Guarulhos–Osasco 3. **Timeline** (40% right) com: - Caixa de busca no topo - 8 cards ordenados cronologicamente - Card #1 destacado por padrão - Painel inferior com detalhes do card selecionado 4. **Player** (bottom) com botões Play/Pause, Anterior/Próximo, scrubber, selector velocidade Ao clicar **Play**, o mapa anima sequencialmente (500ms entre eventos), percorrendo todos os 8 registros. --- ## 13. NOTAS ADICIONAIS - **Dados sensíveis:** Esta ferramenta processa dados de investigação policial. Implemente segurança: - Não persistir dados em localStorage sem criptografia - Avisar ao usuário: "Dados não são salvos. Feche a aba para limpar." - **Acessibilidade:** Use `aria-labels` nos botões, contraste de cores conforme WCAG AA - **Documentação:** Incluir em comentário no HTML a versão do prompt (v2.0) e link para `engenhariadeprompts.ia.br`
📋

Instruções de uso não disponíveis.

Variáveis do prompt
[
[
[
[
[
[
[
[
[
[
[ {
O que os usuários dizem
💬

Ainda sem avaliações. Seja o primeiro!

Faça login para deixar uma avaliação.