Last active
June 6, 2025 22:32
-
-
Save gustavonovaes/f8138228b9bfa3dad36b46d84839840a to your computer and use it in GitHub Desktop.
Filtra crimes em SJC próximos de localização, preencher coordenadas lat long e exporta em WKT.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import fs from "fs"; | |
| import path from "path"; | |
| /* | |
| wget https://www.ssp.sp.gov.br/assets/estatistica/transparencia/spDados/SPDadosCriminais_2025.xlsx -O SPDadosCriminais_2025.xlsx | |
| # SPDadosCriminais_2025.xlsx > SPDadosCriminais_2025.csv | |
| cat SPDadosCriminais_2025.csv | grep "S. JOSÉ DOS CAMPOS" >> SPDadosCriminais_2025_SJC.csv | |
| NOME_DEPARTAMENTO, | |
| NOME_SECCIONAL, | |
| NOME_DELEGACIA, | |
| NOME_MUNICIPIO, | |
| NUM_BO, | |
| ANO_BO, | |
| DATA_REGISTRO, | |
| DATA_OCORRENCIA_BO, | |
| HORA_OCORRENCIA_BO, | |
| DESC_PERIODO, | |
| DESCR_SUBTIPOLOCAL, | |
| BAIRRO, | |
| LOGRADOURO, | |
| NUMERO_LOGRADOURO, | |
| LATITUDE, | |
| LONGITUDE, | |
| NOME_DELEGACIA_CIRCUNSCRIÇÃO, | |
| NOME_DEPARTAMENTO_CIRCUNSCRIÇÃO, | |
| NOME_SECCIONAL_CIRCUNSCRIÇÃO, | |
| NOME_MUNICIPIO_CIRCUNSCRIÇÃO, | |
| RUBRICA, | |
| DESCR_CONDUTA, | |
| NATUREZA_APURADA, | |
| MES_ESTATISTICA, | |
| ANO_ESTATISTICA | |
| */ | |
| const filePath = path.join(process.cwd(), "SPDadosCriminais_2025_SJC.csv"); | |
| const fileContent = fs.readFileSync(filePath, "utf8"); | |
| const COORDS_PARQUE_CIDADE = { | |
| latitude: -23.170518, | |
| longitude: -45.882972, | |
| }; | |
| const COORDS_CASA = { | |
| latitude: -23.232008801951885, | |
| longitude: -45.905322944592626, | |
| }; | |
| const csvData = parseCSVWithStringDelimiter(fileContent, ",") | |
| .filter((columns) => columns.length >= 22) | |
| .filter((columns) => columns[3] === "S.JOSE DOS CAMPOS") | |
| .filter(async (columns, index) => { | |
| const latitudeRaw = columns[14]; | |
| const longitudeRaw = columns[15]; | |
| let latitude = parseFloat(latitudeRaw.replace(",", ".").trim()); | |
| let longitude = parseFloat(longitudeRaw.replace(",", ".").trim()); | |
| if (isNaN(latitude) || isNaN(longitude)) { | |
| const logradouro = columns[12]; | |
| const numeroLogradouro = columns[13]; | |
| const bairro = columns[11]; | |
| const query = | |
| `${logradouro.trim()} ${numeroLogradouro},${bairro},São José dos Campos` | |
| .toUpperCase() | |
| .replaceAll(/ /g, "+"); | |
| try { | |
| const coords = await getLatLong(query); | |
| csvData[index][14] = latitude = coords.latitude; | |
| csvData[index][15] = longitude = coords.longitude; | |
| } catch (err) { | |
| return false; | |
| } | |
| } | |
| const diffLat = Math.abs(latitude - COORDS_CASA.latitude); | |
| const diffLong = Math.abs(longitude - COORDS_CASA.longitude); | |
| return !(diffLat > 0.018 || diffLong > 0.018); | |
| }); | |
| console.log("WKT,name,description"); | |
| for await (const columns of csvData) { | |
| const [ | |
| nomeDepartamento, | |
| nomeSeccional, | |
| nomeDelegacia, | |
| nomeMunicipio, | |
| numBO, | |
| anoBO, | |
| dataRegistro, | |
| dataOcorrenciaBO, | |
| horaOcorrenciaBO, | |
| descPeriodo, | |
| descSubtipoLocal, | |
| bairro, | |
| logradouro, | |
| numeroLogradouro, | |
| latitudeRaw, | |
| longitudeRaw, | |
| nomeDelegaciaCircunscricao, | |
| nomeDepartamentoCircunscricao, | |
| nomeSeccionalCircunscricao, | |
| nomeMunicipioCircunscricao, | |
| rubrica, | |
| descricaoConduta, | |
| naturezaApurada, | |
| mesEstatistica, | |
| anoEstatistica, | |
| ] = columns; | |
| let latitude = parseFloat(latitudeRaw.replace(",", ".").trim()); | |
| let longitude = parseFloat(longitudeRaw.replace(",", ".").trim()); | |
| if (isNaN(latitude) || isNaN(longitude) || !latitude || !longitude) { | |
| try { | |
| const query = | |
| `${logradouro.trim()} ${numeroLogradouro} ${bairro}, São José dos Campos` | |
| .toUpperCase() | |
| .replaceAll(/ /g, "+"); | |
| const res = await getLatLong(query); | |
| latitude = res.latitude; | |
| longitude = res.longitude; | |
| } catch (err) { | |
| latitude = 0; | |
| longitude = 0; | |
| } | |
| } | |
| const [dia, mes, ano] = dataOcorrenciaBO.split("/"); | |
| const dataBO = new Date(ano, mes - 1, dia).toISOString(); | |
| const data = | |
| new Date(dataBO).toISOString().substring(0, 10) + | |
| (descPeriodo && descPeriodo != "NULL" ? ` (${descPeriodo})` : ""); | |
| const WKT = `POINT (${longitude.toFixed(6)} ${latitude.toFixed(6)})`; | |
| const name = `${data} - ${naturezaApurada} - ${logradouro}, N ${numeroLogradouro} - ${descSubtipoLocal}`; | |
| const description = Object.entries({ | |
| logradouro: `${logradouro}, N ${numeroLogradouro}, ${bairro} ${ | |
| descSubtipoLocal == "NULL" ? "" : "(" + descSubtipoLocal + ")" | |
| }`, | |
| rubrica, | |
| descricaoConduta, | |
| naturezaApurada, | |
| data, | |
| dataRegistro, | |
| numBO, | |
| }) | |
| .reduce((acc, [key, value]) => { | |
| if (value !== "NULL") { | |
| acc.push(`${key}: ${value}`); | |
| } | |
| return acc; | |
| }, []) | |
| .join(", "); | |
| console.log(`"${WKT}","${name}","${description}"`); | |
| } | |
| function parseCSVWithStringDelimiter(csvString, delimiter = ",") { | |
| const result = []; | |
| const lines = csvString.split("\n"); | |
| for (const line of lines) { | |
| const values = []; | |
| let currentValue = ""; | |
| let inQuotes = false; | |
| for (const char of line) { | |
| if (char === '"') { | |
| inQuotes = !inQuotes; | |
| } else if (char === delimiter && !inQuotes) { | |
| values.push(currentValue.trim()); | |
| currentValue = ""; | |
| } else { | |
| currentValue += char; | |
| } | |
| } | |
| values.push(currentValue.trim()); | |
| result.push(values); | |
| } | |
| return result; | |
| } | |
| function getLatLong(query) { | |
| return fetch( | |
| `https://nominatim.openstreetmap.org/search?format=json&q=${query}` | |
| ) | |
| .then((response) => response.json()) | |
| .then((data) => { | |
| return { | |
| latitude: parseFloat(data[0].lat), | |
| longitude: parseFloat(data[0].lon), | |
| }; | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment