Geolocalização com Flask e SQLite: Crie uma API para Gerenciar Usuários
A geolocalização é uma funcionalidade essencial em muitas aplicações modernas. Seja para rastrear dispositivos, fornecer informações locais ou gerenciar dados de usuários, é crucial implementar uma solução eficiente. Neste post, mostramos como criar uma API em Python usando Flask e SQLite, permitindo o cadastro e gerenciamento de usuários com dados de localização.
Estrutura da API
Nossa API oferece operações CRUD (Create, Read, Update, Delete) para gerenciar uma tabela USUARIO. A tabela inclui os campos:
1 2 3 4 5 |
'id': Identificador único (autoincrementado). 'nome': Nome do usuário. 'tipo': Tipo do usuário. 'lat': Latitude. 'lng': Longitude. |
Para implementar essa API, usamos Flask para criar rotas HTTP e SQLite para armazenar os dados.
Configurando o Banco de Dados
O banco de dados é criado automaticamente com a estrutura adequada ao inicializar o sistema. A rota /initdb executa os scripts SQL definidos nos arquivos drop_schema.sql e create_schema.sql para garantir que a estrutura esteja pronta.
1 2 3 4 5 6 7 |
CREATE TABLE USUARIO( id integer PRIMARY KEY AUTOINCREMENT, nome text, tipo text, lat real, lng real ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
projeto |-static | |-icone.png | |-maps.js | |-styles.css | |-templates |-index.html app.py create_schema.sql database.db drop_schema.sql leiame.txt |
Map.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
function existeLocalStorage(){ var savedUserId = localStorage.getItem('userId'); if (savedUserId){ return true } else { return false } } function existeUsuarioNoServidor(id) { var url = 'http://localhost:5000/usuarios/' + id; console.log('Verificando usuário no servidor: ' + url); return fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json' } }) .then(response => { if (response.ok) { console.log('Usuário encontrado no servidor: ID ' + id); return true; // Usuário existe } else if (response.status === 404) { console.log('Usuário não encontrado: ID ' + id); return false; // Usuário não existe } else { throw new Error('Erro ao verificar usuário: ' + response.status); } }) .catch(error => { console.log("Erro: " + error.message); return false; // Em caso de erro, assume que o usuário não existe }); } function criaUsuarioLocal(lat,lng){ console.log("Criando Localmente Usuário") localStorage.setItem('userName',document.getElementById('userNameInput').value); localStorage.setItem('userlatitude', lat); localStorage.setItem('userlongitude', lng); } function atualizaUsuarioLocal(lat,lng){ localStorage.setItem('userName',document.getElementById('userNameInput').value); localStorage.setItem('userlatitude', lat); localStorage.setItem('userlongitude', lng); } function criarUsuarioNoServidor(nome,lat,lng) { console.log("criarUsuarioNoServidor ("+nome+','+lat+','+lng+')') var url = 'http://localhost:5000/usuarios'; console.log('criarUsuarioServidor => url('+url+')') var data = { lat: lat, lng: lng, nome: nome, tipo: "pessoa" }; console.log('criarUsuarioServidor => data('+JSON.stringify(data)+')') fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => { console.log("Status da resposta: ", response.status); return response.json().then(data => { console.log("Resposta do servidor:", data); if (response.ok) { return data; } else { throw new Error('Erro ao tentar criar usuário'); } }); }) .then(data => { // Aqui assumimos que o servidor está retornando o ID do usuário criado var userId = data.id; console.log("ID do usuário criado: ", userId); // Armazenar o ID no localStorage localStorage.setItem('userId', userId); alert("Usuário criado com sucesso no Servidor"); }) .catch(error => { console.log("Erro: " + error.message); }); } function validaInformacoes(nome,lat,lng){ ok = true campos = [] if (nome == null){ campos.push('nome') ok = false } if (lat == null){ campos.push('lat') ok = false } if (lng == null){ campos.push('lat') ok = false } if (!ok){ console.log("validaInformacoes => Campos null:",campos) } return ok } function getLocation() { console.log("imprimindo aqui") var latitude = null; var longitude = null; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { latitude = position.coords.latitude; longitude = position.coords.longitude; localStorage.setItem('userlatitude', latitude); localStorage.setItem('userlongitude', longitude); document.getElementById("location").innerHTML = "Latitude: " + latitude + "<br>Longitude: " + longitude; fetch('/geolocation', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ latitude: latitude, longitude: longitude }) }); var data = null; var map = null; var existeLocalmente = existeLocalStorage() var existeNoServidor = false if (existeLocalmente){ console.log("Usuário Existe Localmente") localStorage.setItem('userName',document.getElementById('userNameInput').value); existeNoServidor = existeUsuarioNoServidor(localStorage.getItem('userId')) if (existeNoServidor){ console.log("Usuário Existe no SERVIDOR") atualizaUsuarioLocal(latitude,longitude) if (validaInformacoes(localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude'))){ atualizaNoServidor() //atualiza objetos na pagina recarregarPagina() } else { console.log("Usuário deve ser atualizado mas alguma informação está null") } } else { console.log("Usuário NÃO Existe no SERVIDOR") if (validaInformacoes(localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude'))){ criarUsuario(localStorage.getItem('userId'),localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude')) } else { console.log("Usuário deve ser criado mas alguma informação está null") } } } else { console.log("Usuário NÃO Existe Localmente") criaUsuarioLocal(latitude,longitude) existeNoServidor = existeUsuarioNoServidor(localStorage.getItem('userId')) if (existeNoServidor){ console.log("Usuário Existe no SERVIDOR") criarUsuarioNoServidor(localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude')) } else { console.log("Usuário NÃO Existe no SERVIDOR") if (validaInformacoes(localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude'))){ criarUsuarioNoServidor(localStorage.getItem('userName'),localStorage.getItem('userlatitude'),localStorage.getItem('userlongitude')) } else { console.log("Usuário deve ser criado mas alguma informação está null") } } } }); } else { console.log("Geolocalização não é suportada por este navegador."); } } function atualizaNoServidor(){ // Criar o objeto JSON com os dados data = { lat: localStorage.getItem('userlatitude'), lng: localStorage.getItem('userlongitude'), nome: localStorage.getItem('userName'), tipo: "pessoa" }; // Enviar os dados para o servidor fetch("http://localhost:5000/usuarios/"+localStorage.getItem('userId'), { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => response.json()) .then(data => { alert('Sucesso ao atualizar usuário no Servidor ('+id+','+nome+','+lat+','+lng+')'); }) .catch((error) => { console.log('Erro ao tentar atualizar no Servidor ('+id+','+nome+','+lat+','+lng+')'); }); } function recarregarPagina(){ var pessoaIcon = L.icon({ iconUrl: 'static/pessoa.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); var estacionamentoIcon = L.icon({ iconUrl: 'static/estacionamento.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); var cidadeIcon = L.icon({ iconUrl: 'static/cidade.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); var aeroportoIcon = L.icon({ iconUrl: 'static/aeroporto.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); var turismoIcon = L.icon({ iconUrl: 'static/turismo.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); var livrariaIcon = L.icon({ iconUrl: 'static/livraria.png', iconSize: [32, 32], // Ajuste o tamanho conforme necessário iconAnchor: [22, 38], // Ajuste o ponto de ancoragem conforme necessário popupAnchor: [-3, -76] // Ajuste o ponto do popup conforme necessário }); // Inicializa o mapa map = L.map('map').setView([-15.7801, -47.9292], 4); // Centra o mapa no Brasil // Adiciona a camada do mapa L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map); // Obtém os dados dos usuários fetch('/usuarios') .then(response => response.json()) .then(data => { data.forEach(user => { var markerIcon = null; // Escolhe o ícone apropriado baseado no tipo do usuário switch (user.tipo) { case "estacionamento": markerIcon = estacionamentoIcon; break; case "cidade": markerIcon = cidadeIcon; break; case "aeroporto": markerIcon = aeroportoIcon; break; case "turismo": markerIcon = turismoIcon; break; case "livraria": markerIcon = livrariaIcon; break; case "pessoa": markerIcon = pessoaIcon; break; default: markerIcon = pessoaIcon; // Ou um ícone padrão se necessário break; } // Cria o marcador com o ícone apropriado var marker = L.marker([user.lat, user.lng], { icon: markerIcon }).addTo(map); // Cria um elemento HTML para o popup var popupContent = document.createElement('div'); popupContent.id = 'obj' + user.id; // Define o ID do elemento HTML popupContent.innerHTML = "<b>Nome:</b> " + user.nome + "<br><b>Id:</b> " + user.id + "<br><b>Tipo:</b> " + user.tipo; // Vincula o elemento HTML ao popup do marcador marker.bindPopup(popupContent); }); }) .catch(error => { console.error('Erro ao buscar dados dos usuários:', error); }); } document.addEventListener("DOMContentLoaded", function () { recarregarPagina() }); |
Style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
body { font-family: Arial, sans-serif; margin: 0; padding: 0; } h1 { text-align: center; margin: 20px 0; } #map { height: 600px; /* Ajuste a altura conforme necessário */ } |
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!DOCTYPE html> <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Mapa de Localizações</title> <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" /> <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" /> </head> <body> <h1>Mapa</h1> <input type="text" id="userNameInput" placeholder="Digite seu nome"> <button onclick="getLocation()">Salvar Nome</button> <div id="map"></div> <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> <script src="{{ url_for('static', filename='map.js') }}"></script> </body> </html> |
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
from flask import Flask, render_template, jsonify, request import sqlite3 app = Flask(__name__) # Configuração do banco de dados SQLite DATABASE = 'database.db' # Faz conexão CONNECTION = sqlite3.connect(DATABASE,check_same_thread=False) CURSOR = CONNECTION.cursor() @app.route('/geolocation', methods=['POST']) def geolocation(): data = request.get_json() # Faça algo com a geolocalização aqui return jsonify({'status': 'success'}) # Função para conectar ao banco de dados def get_db(): db = sqlite3.connect(DATABASE) db.row_factory = sqlite3.Row return db # Função para criar a tabela de dados, se ela não existir def executa_schema_se_nao_existir_tabela(): with app.app_context(): db = get_db() with app.open_resource('drop_schema.sql', mode='r') as f: db.cursor().executescript(f.read()) with app.open_resource('create_schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() # Rota para criar a tabela de dados @app.route('/initdb') def inicializa_bd(): executa_schema_se_nao_existir_tabela() return 'Database inicializada' @app.route('/estrutura_usuario', methods=['GET']) def estrutura_usuario(): try: db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM pragma_table_info('usuario')") dados = cursor.fetchall() return jsonify([dict(row) for row in dados]) except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() @app.route('/') @app.route('/index') def home(): return render_template('index.html') @app.route('/rotas') def rotas(): conteudo = """ <h1>Bem-vindo à API CRUD com Flask</h1> <p>Esta API permite que você execute operações CRUD (Create, Read, Update, Delete) em uma base de dados SQLite.</p> <p>Rotas disponíveis:</p> <ul> <li>POST /usuarios - Adiciona um novo dado. Envie um JSON com os campos 'nome' ,'tipo', 'lat' e 'lng', o campo 'id' é autoincrementado.</li> <li>GET /usuarios - Retorna todos os dados na base de dados.</li> <li>GET /usuarios/{id} - Retorna um dado específico por ID.</li> <li>PUT /usuarios/{id} - Atualiza um dado existente por ID. Envie um JSON com os campos 'nome' , 'lat' e 'lng'.</li> <li>DELETE /usuarios/{id} - Deleta um dado existente por ID.</li> </ul> """ return conteudo # Consulta de todos os usuários @app.route('/usuarios', methods=['GET']) def retorna_usuarios(): try: db = get_db() cursor = db.cursor() cursor.execute('SELECT * FROM usuario') dados = cursor.fetchall() return jsonify([dict(row) for row in dados]) except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() # Consulta de 1 usuário @app.route('/usuarios/<int:id>', methods=['GET']) def retorna_usuario(id): try: db = get_db() cursor = db.cursor() cursor.execute('SELECT * FROM usuario WHERE id = ?', (id,)) dado = cursor.fetchone() if dado: return jsonify(dict(dado)) else: return jsonify({'error': 'Dado não encontrado'}), 404 except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() # Alterar de 1 usuário @app.route('/usuarios/<int:id>', methods=['PUT']) def alterar_usuario(id): nome = request.json.get('nome') tipo = request.json.get('tipo') latitude = request.json.get('lat') longitude = request.json.get('lng') if not nome or not latitude or not longitude: return jsonify({'error': 'Nome, tipo, latitude e longitude são obrigatórios'}), 400 try: db = get_db() cursor = db.cursor() cursor.execute('UPDATE usuario SET nome = ?, tipo = ?, lat = ?, lng = ? WHERE id = ?', (nome, tipo, latitude, longitude, id)) db.commit() return jsonify({'message': 'Dado atualizado com sucesso!'}) except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() @app.route('/usuarios', methods=['POST', 'GET']) def novo_usuario(): if request.method == 'POST': nome = request.json.get('nome') tipo = request.json.get('tipo') latitude = float(request.json.get('lat')) longitude = float(request.json.get('lng')) if not nome or not tipo or not latitude or not longitude: return jsonify({'error': 'nome:text, tipo:text latitude:float e longitude:float são obrigatórios'}), 400 try: db = get_db() cursor = db.cursor() cursor.execute('INSERT INTO usuario (nome, tipo, lat, lng) VALUES (?, ?, ?, ?)', (nome, tipo, latitude, longitude)) db.commit() # Obter o ID do usuário recém-criado user_id = cursor.lastrowid # Retornar o ID do usuário junto com a mensagem de sucesso return jsonify({'message': 'Usuário adicionado com sucesso!', 'id': user_id}), 201 except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() elif request.method == 'GET': return home() # Excluir Usuário @app.route('/usuarios/<int:id>', methods=['DELETE']) def exclui_usuario(id): try: id = int(id) db = get_db() cursor = db.cursor() cursor.execute('DELETE FROM usuario WHERE id = ?', (id,)) db.commit() return jsonify({'message': 'Usuário deletado com sucesso!'}) except sqlite3.Error as e: return jsonify({'error': str(e)}), 500 finally: db.close() if __name__ == '__main__': app.run(port=5000,host='localhost',debug=True) |
Nossa API oferece as seguintes rotas para interagir com os dados:
1 2 3 4 5 |
POST /usuarios - Adiciona um novo dado. Envie um JSON com os campos 'nome' ,'tipo', 'lat' e 'lng', o campo 'id' é autoincrementado. GET /usuarios - Retorna todos os dados na base de dados. GET /usuarios/{id} - Retorna um dado específico por ID. PUT /usuarios/{id} - Atualiza um dado existente por ID. Envie um JSON com os campos 'nome' , 'lat' e 'lng'. DELETE /usuarios/{id} - Deleta um dado existente por ID. |
Adicione novos usuários enviando um JSON com os campos obrigatórios:
1 2 3 4 5 6 |
{ "nome": "João Silva", "tipo": "pessoa", "lat": -23.5489, "lng": -46.6388 } |
Na opção Salvar Nome o browser perguntará se você permiti dar o acesso da sua geolocalização.
Caso concorde será salvo na sua base de dados a sua geolocalização no mapa, mas para visualizar terá que atualizar a página.
Na rota “/usuarios” visualize todos os registros na base de dados.
Tomei a liberdade de adicionar alguns ícones no projeto para distinguir os seguintes tipos:
1 2 3 4 5 6 |
estacionamento cidade aeroporto turismo livraria pessoa |
Basta cadastrar um dos tipos acima mencionados para colocar a geolocalização com um ícone respectivo ao tipo.
Use o Postman para executar as API’s.
Visualização
Conclusão
Com esta API, você inicia o trabalho de geolocalização e gerenciamento de usuários em suas aplicações.
Aproveite o código fornecido para construir soluções mais inteligentes e conectadas!