Skip to content

feat: Add endpoint to search shelters by lat, lng and radius (meters)… #121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions scripts/address_lat_long.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pip3 install psycopg2"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import psycopg2\n",
"import requests\n",
"import time\n",
"\n",
"# Função para obter latitude e longitude de um endereço usando a API do Google Maps\n",
"def get_lat_lng(address):\n",
" print(address)\n",
" api_key = \"YOUR GOOGLE KEY\" # Insira sua chave de API, Ativar a API do Google Maps (Geocoding API)\n",
" url = f\"https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={api_key}\"\n",
" response = requests.get(url) \n",
" data = response.json()\n",
" if data[\"status\"] == \"OK\":\n",
" location = data[\"results\"][0][\"geometry\"][\"location\"]\n",
" return location[\"lat\"], location[\"lng\"]\n",
" else:\n",
" return None, None\n",
"\n",
"# Função para atualizar a tabela shelters com latitude e longitude\n",
"def update_lat_lng(conn):\n",
" cursor = conn.cursor()\n",
" cursor.execute(\"SELECT id, address FROM shelters WHERE latitude IS NULL AND longitude IS NULL\")\n",
" rows = cursor.fetchall()\n",
" for row in rows:\n",
" shelter_id, address = row\n",
" lat, lng = get_lat_lng(address)\n",
" if lat is not None and lng is not None:\n",
" cursor.execute(\"UPDATE shelters SET latitude = %s, longitude = %s WHERE id = %s\", (lat, lng, shelter_id))\n",
" conn.commit()\n",
" print(f\"Latitude e longitude atualizadas para o abrigo ID {shelter_id}\")\n",
" else:\n",
" print(f\"Falha ao obter latitude e longitude para o abrigo ID {shelter_id}\")\n",
" time.sleep(5) # Intervalo de espera de 5 segundos entre as chamadas para a API do Google Maps\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conexão bem-sucedida ao banco de dados\n",
"R. Papa João Xxiii, 316 - São Vicente, Cachoeirinha - Rs, 94910-170\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213000\n",
"Rua Da Gruta, 240 - Glória, Porto \n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213010\n",
"Rua Cipó, 450 - Vila Ipiranga, Porto Alegre (ginásio Da Ielb)\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213009\n",
"Av. Loureiro Da Silva, 150 - Cidade Baixa, Porto Alegre - Rs, 90010-420\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213008\n",
"Rua Joao dallegrave, 130 - Morro Santana\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213007\n",
"Rua 3, Nº 178 - Vila Das Laranjeiras - Morro Santana - Porto Alegre\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213013\n",
"R. Gonçalves Dias, 700 - Menino Deus, Porto Alegre RS, 90130-060\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213012\n",
"R. Dona Alzira, 417 - Sarandi, Porto Alegre - Rs, 91110-010\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213011\n",
"R. Chico Pedro, 390 - Camaquã, Porto Alegre\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213006\n",
"Rua Fernando Pessoa, Nº 300, Bairro Vila Nova - Porto Alegre\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213005\n",
"Av. Icaraí, 1168 - Cristal, Porto Alegre - Rs, 90810-000\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213004\n",
"Av. Protásio Alves, 6220 - Petrópolis, Porto Alegre - Rs, 91310-001\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213003\n",
"Rua Coronel Joaquim Pedro Salgado, 80 - Rio Branco, Porto Alegre - Rs, 90420-060\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213002\n",
"Av. Bento Gonçalves, 7196 - Agronomia - Porto Alegre - Rs\n",
"Latitude e longitude atualizadas para o abrigo ID 7f21ccc7-d1de-4a23-b710-260761213001\n",
"Conexão com o banco de dados fechada\n"
]
}
],
"source": [
"\n",
"# Conectar ao banco de dados PostgreSQL\n",
"try:\n",
" conn = psycopg2.connect(\n",
" dbname=\"sos_rs\",\n",
" user=\"root\",\n",
" password=\"root\",\n",
" host=\"127.0.0.1\",\n",
" port=\"5432\"\n",
" )\n",
" print(\"Conexão bem-sucedida ao banco de dados\")\n",
" update_lat_lng(conn)\n",
"except psycopg2.Error as e:\n",
" print(\"Erro ao conectar ao banco de dados PostgreSQL:\", e)\n",
"finally:\n",
" if conn is not None:\n",
" conn.close()\n",
" print(\"Conexão com o banco de dados fechada\")\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
11 changes: 11 additions & 0 deletions src/shelter/shelter.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,15 @@ export class ShelterController {
throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400);
}
}

@Post('geolocation')
async geolocation(@Body() body) {
try {
const data = await this.shelterService.getSheltersBylocation(body);
return new ServerResponse(200, 'Successfully get shelters by location', data);
} catch (err: any) {
this.logger.error(`Failed to get shelters by location: ${err}`);
throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400);
}
}
}
42 changes: 41 additions & 1 deletion src/shelter/shelter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { PrismaService } from '../prisma/prisma.service';
import { SupplyPriority } from '../supply/types';
import { SearchSchema } from '../types';
import { ShelterSearch, parseTagResponse } from './ShelterSearch';
import { ShelterSearchPropsSchema } from './types/search.types';
import { GeolocationFilterSchema, ShelterSearchPropsSchema } from './types/search.types';
import {
CreateShelterSchema,
FullUpdateShelterSchema,
Expand Down Expand Up @@ -209,6 +209,33 @@ export class ShelterService implements OnModuleInit {
}));
}

async getSheltersBylocation(body: z.infer<typeof GeolocationFilterSchema>) {
const {
latitude: lat,
longitude: lng,
radiusInMeters: radius,
} = GeolocationFilterSchema.parse(body);

const shelters = await this.prismaService.shelter.findMany();
const sheltersWithinRadius: any = [];

shelters.forEach(async shelter => {
if (!shelter.latitude || !shelter.longitude) {
return;
}
const distance = await this.calculateDistance(lat, lng, shelter.latitude, shelter.longitude);
const radiusInKm = radius / 1000;
if (distance <= radiusInKm) {
sheltersWithinRadius.push(shelter);
}
});

return {
shelters: sheltersWithinRadius,
};
}


private loadVoluntaryIds() {
this.prismaService.supplyCategory
.findMany({
Expand All @@ -222,4 +249,17 @@ export class ShelterService implements OnModuleInit {
this.voluntaryIds.push(...resp.map((s) => s.id));
});
}

private calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371;
const dLat = (lat2 - lat1) * (Math.PI / 180);
const dLon = (lon2 - lon1) * (Math.PI / 180);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
}

}