Skip to content

Instantly share code, notes, and snippets.

@gustavonovaes
Last active June 6, 2025 22:32
Show Gist options
  • Select an option

  • Save gustavonovaes/f8138228b9bfa3dad36b46d84839840a to your computer and use it in GitHub Desktop.

Select an option

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.
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