Visão Geral
Neste tópico vou apresentar uma solução de Trajeto/Rota editável usando Google Maps no Formulário.
Serão utilizados os seguintes recursos:
- Biblioteca e Chave de API do Google Maps
- Uma tabela no banco de dados
- Formulário
- Códigos JavaScript.
Este exemplo funcionará da seguinte maneira:
-
O usuário informa o local de origem e de destino e clica no primeiro botão para calcular a rota usando a API do Google Maps.
-
Depois, o usuário arrasta partes da rota para pontos específicos do mapa para gerar desvios (mesmo comportamento do App Google Maps).
-
Cada vez que um ponto é arrastado para gerar um desvio, o mapa cria um novo “Waypoint”.
-
Ao clicar no segundo botão, a origem, destino e os Waypoints são gravados no banco de dados.
-
Para carregar a rota previamente configurarada, quando a página for aberta novamente, a rota é calculada novamente, mas dessa vez especificando os Waypoints. Os Waypoints serão os responsáveis por carregar o formato customizado da rota.
Você pode conferir o exemplo em funcionamento clicando neste link.
Chave de API
Para obter ou configurar uma chave de API do Google Maps, é necessário acessar a página Credenciais - APIs e Servidor do Google Cloud Platform.
Neste exemplo, vamos precisar das seguintes APIs habilitadas para a chave:
- Directions API
- Geocoding API
- Maps JavaScript API
Banco de Dados
Para gravar os dados do percurso, vamor criar uma tabela bem simples, e inicializa-la com o registro que vamos apresentar na tela:
CREATE TABLE routes
(
id INT NOT NULL,
name TEXT NOT NULL,
origin TEXT,
destination TEXT,
waypoints TEXT,
CONSTRAINT pk_routes_id PRIMARY KEY (id)
);
-- Insere registro para demonstração
INSERT INTO routes (id, name, origin, destination)
VALUES (1, 'Rota de Teste', 'Latromi', 'Eco Bike Park Vento Negro')
Formulário
Crie um Formulário do LATROMI com a seguinte estrutura para hospedar o mapa:
Variáveis
Nome | Comentários |
---|---|
apiKey | Inicialize essa variável com a chave de API no evento Load do Formulário. |
Campos
Nome | Comentários |
---|---|
origin | Critério de pesquisa do local de origem |
destination | Critério de pesquisa do local de destino |
applyButton | Botão para realizar o cálculo da rota |
saveButton | Botão para salvar a rota calculada |
waypoints | Recebe os desvios de rota, gerados para cada ponto editado no trajeto |
path | Recebe o caminho completo do trajeto, formato por uma lista de coordenadas |
JavaScript
Adicione um novo arquivo JavaScript no Formulário, e nele informe o seguinte código:
const myForm = (function () {
let map;
let directionsService;
let directionsRenderer;
// Adiciona Callbacks no Formulário
latromi.formManager.setOnFormCreatedCallback(onFormCreated);
latromi.formManager.setOnEventFiringCallback(onEventFiring);
latromi.formManager.setOnFieldValueChangedCallback(onFieldValueChanged);
// Ocorre logo após o form ser inicializado
function onFormCreated(ev) {
const apiKey = ev.form.getVariableValue('apiKey');
latromi.extensions.loadScripts(['https://maps.googleapis.com/maps/api/js?libraries=places&v=weekly&key=' + apiKey], initMap);
}
// Ocorre quando um evento é disparado no Form
function onEventFiring(ev) { }
// Ocorre quando o valor de um campo é alterado no Form
function onFieldValueChanged(ev) { }
// Função de callback para inicialização do mapa.
// Essa função é chamada logo após a conclusão do carregamento das bibliotecas do Google Maps.
function initMap() {
// Initializa o mapa, passando o elemento "div" onde ele será renderizado
map = new google.maps.Map(document.getElementById("map"));
// Inicializa os serviços que fazem calculo e a apresentação das rotas
directionsService = new google.maps.DirectionsService();
directionsRenderer = new google.maps.DirectionsRenderer({
draggable: true, // Essa propriedade habilita a criação de desvios na rota
map
});
calculateRoute();
// Adiciona callback no "directions_changed" para interceptar as mudanças na rota
directionsRenderer.addListener("directions_changed", () => {
const directions = directionsRenderer.getDirections();
if (directions) {
updateOuput(directions);
}
});
}
// Atualiza campos com "waypoints" (Desvios) e "path" (Rota)
function updateOuput(directionResult) {
const form = latromi.formManager.getFormInstance();
form.setFieldValue('waypoints', formatCoordinates(getWaypointsCoordinates(directionResult)));
form.setFieldValue('path', formatCoordinates(getPathCoordinates(directionResult)));
form.setFieldValue('origin', getStartAddress(directionResult));
form.setFieldValue('destination', getEndAddress(directionResult));
}
// Função que calcula a rota
function calculateRoute() {
if (!map) throw 'O mapa ainda não foi carregado.';
const form = latromi.formManager.getFormInstance();
const origin = form.getFieldValue('origin'); // Pega valor do TextBox "origin"
const destination = form.getFieldValue('destination'); // Pega valor do TextBox "destination"
const waypoints = parseStoredCoordinates(form.getFieldValue('waypoints')); // Pega valor do TextBox "waypoints"
// Se origem e destino não foram informados, não faz nada
if (!origin || !destination) return;
const request = {
origin: origin,
destination: destination,
waypoints: waypoints,
travelMode: google.maps.TravelMode.DRIVING,
avoidTolls: true,
};
directionsService
.route(request)
.then((result) => {
directionsRenderer.setDirections(result);
})
.catch((e) => {
alert("Could not display directions due to: " + e);
});
}
// Transforma o valor fornecido no parâmetro "text" em um array de LatLng:
// Formato fornecido:
// (lat,lng),(lat,lng)
//
// Formato de resposta:
// [
// {lat: number, lng: number},
// {lat: number, lng: number}
// ]
function parseStoredCoordinates(text) {
const result = [];
if (text) {
const reg = /[-+]?\d+(\.\d+)?,\s?[-+]?\d+(\.\d+)?\b/g;
while ((match = reg.exec(text)) !== null) {
const coordArray = match[0].split(',');
result.push({
location: {
lat: parseFloat(coordArray[0]),
lng: parseFloat(coordArray[1])
}, stopover: false
});
}
}
return result;
}
// Formata o array de coordenadas fornecido no parâmetro "coordinates" para o formato (lat,lng),(lat,lng)
function formatCoordinates(coordinates) {
const arr = []
if (coordinates) {
for (let index = 0; index < coordinates.length; index++) {
arr.push("(" + coordinates[index].lat() + "," + coordinates[index].lng() + ")");
}
}
return arr.join(",");
}
// Retorna todas as coordenadas que compõe o trajeto (path) do resultado
// do cálculo de rota fornecido no parâmetro "directionResult".
function getPathCoordinates(directionResult) {
const myroute = directionResult.routes[0];
if (!myroute) return;
return myroute.overview_path;
}
// Retorna as coordenadas dos waypoints (desvios) do trajeto do resultado
// do cálculo de rota fornecido no parâmetro "directionResult".
function getWaypointsCoordinates(directionResult) {
const myroute = directionResult.routes[0];
if (!myroute) return;
const coordinates = [];
for (let legIndex = 0; legIndex < myroute.legs.length; legIndex++) {
const leg = myroute.legs[legIndex];
// Armazena waipoints
for (let wpIndex = 0; wpIndex < leg.via_waypoint.length; wpIndex++) {
coordinates.push(leg.via_waypoint[wpIndex].location);
}
}
return coordinates;
}
// Retorna o endereço de origem do trajeto do resultado
// do cálculo de rota fornecido no parâmetro "directionResult".
function getEndAddress(directionResult, legIndex) {
const myroute = directionResult.routes[0];
if (!myroute) return;
const myLeg = myroute.legs[legIndex || 0];
if (!myLeg) return;
return myLeg.end_address;
}
// Retorna o endereço de destino do trajeto do resultado
// do cálculo de rota fornecido no parâmetro "directionResult".
function getStartAddress(directionResult, legIndex) {
const myroute = directionResult.routes[0];
if (!myroute) return;
const myLeg = myroute.legs[legIndex || 0];
if (!myLeg) return;
return myLeg.start_address;
}
// Todas as funções acima são "privadas", e não pode ser chamadas externamente.
// A única função que pode ser chamada de externamente são as que compões o resultado a seguir:
return {
// Faz o calculo de rota uilizando os campos
// do formulário como critérios de pesquisa.
route: calculateRoute
}
})();
Eventos
Form: Load
Adicione uma instruação do tipo Criar Record para carregar o registro que inserimos na nossa tabela:
SELECT * FROM routes WHERE id = 1
Em seguida adicione uma instrução Popular Campos e Variáveis. Preencha os campos
“origin”, “destination” e “waypoints” com os suas respectivas colunas.
Preencha também a variável “apiKey” com o valor da sua chave de API.
applyButton: Click
No evento click do botão, utilize uma instrução do tipo Popular Campos e Variáveis para limpar o campo “waypoints”.
Em seguida, adicione uma instrução do tipo Executar código JavaScript, e informe o código a seguir:
myForm.route();
saveButton: Click
Por fim, adicionei uma instrução Executar comando SQL para salvar a rota no banco de dados:
UPDATE routes
SET origin = '{?INPUT origin}'
, destination = '{?INPUT destination}'
, waypoints = NULLIF('{?INPUT waypoints}', '')
WHERE id = 1
Referências
Você encontra a documentação completa da API “Directions” do Google Maps na página a seguir: