diff --git a/scripts/address_lat_long.ipynb b/scripts/address_lat_long.ipynb new file mode 100644 index 00000000..5a14f292 --- /dev/null +++ b/scripts/address_lat_long.ipynb @@ -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 +} diff --git a/src/shelter/shelter.controller.ts b/src/shelter/shelter.controller.ts index 24603857..ada36f4f 100644 --- a/src/shelter/shelter.controller.ts +++ b/src/shelter/shelter.controller.ts @@ -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); + } + } } diff --git a/src/shelter/shelter.service.ts b/src/shelter/shelter.service.ts index f9c853d8..7c42e853 100644 --- a/src/shelter/shelter.service.ts +++ b/src/shelter/shelter.service.ts @@ -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, @@ -209,6 +209,33 @@ export class ShelterService implements OnModuleInit { })); } + async getSheltersBylocation(body: z.infer) { + 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({ @@ -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; + } + }