13) Visualização de Trajetos da Aplicação

Esta documentação explica como o app deve listar as aplicações que possuem trajeto e, depois, carregar as coordenadas para renderizar no Google Maps.

Somente versão Produtor Endpoint 1: listar_aplicacoes_com_trajeto Endpoint 2: listar_trajeto_aplicacao Mapa: Google Maps

1) Fluxo no app

  1. App abre a tela de trajetos.
  2. App chama listar_aplicacoes_com_trajeto.
  3. API retorna as aplicações que possuem trajeto.
  4. Usuário escolhe uma aplicação, por exemplo: Aplicação do dia 08/05/2026.
  5. App chama listar_trajeto_aplicacao com o id_aplicacao.
  6. API retorna os pontos.
  7. App renderiza a linha do trajeto no Google Maps.

2) Regra importante

Estes endpoints devem funcionar somente para a versão do app Produtor. Caso o device seja de outro tipo de conta, a API deve retornar erro de permissão.

3) Endpoint 1 — Listar aplicações com trajeto

POST https://agroecologia.grupoekos.com.br/router/action.php

Headers

X-APP-KEY: <APP_KEY_FIXA_DO_APP>
X-DEVICE-TOKEN: <DEVICE_TOKEN_DO_APARELHO>

Body

action=apiG10
f=listar_aplicacoes_com_trajeto
id_g10_participacao=2

Resposta esperada

{
  "success": true,
  "msg": "Aplicações com trajeto carregadas com sucesso.",
  "data": {
    "id_g10_participacao": 2,
    "cliente_id": 664,
    "total_aplicacoes": 2,
    "aplicacoes": [
      {
        "id_aplicacao": 1,
        "data_aplicacao": "2026-05-08",
        "hora_aplicacao": "08:00:00",
        "label": "Aplicação do dia 08/05/2026 às 08:00",
        "total_trajetos": 1,
        "total_pontos": 240,
        "device_id_trajeto": 285,
        "dispositivo_apelido": "Celular do Produtor",
        "account_type_trajeto": "produtor"
      }
    ]
  }
}

Campos do dispositivo no trajeto

Cada aplicação listada pode retornar também informações do aparelho que realizou o trajeto. Esses campos ajudam o app a exibir para o produtor qual dispositivo registrou aquela aplicação.

4) Endpoint 2 — Listar trajeto da aplicação

POST https://agroecologia.grupoekos.com.br/router/action.php

Headers

X-APP-KEY: <APP_KEY_FIXA_DO_APP>
X-DEVICE-TOKEN: <DEVICE_TOKEN_DO_APARELHO>

Body

action=apiG10
f=listar_trajeto_aplicacao
id_aplicacao=1

Resposta esperada

{
  "success": true,
  "msg": "Trajeto da aplicação carregado com sucesso.",
  "data": {
    "id_aplicacao": 1,
    "id_g10_participacao": 2,
    "cliente_id": 664,
    "total_trajetos": 1,
    "trajetos": [
      {
        {
        "id_trajeto": 5,
        "client_event_id": "abc123",
        "status": "finalizado",
        "dispositivo": {
          "device_id": 285,
          "apelido": "Celular do Produtor",
          "account_type": "produtor"
        },
        "inicio": {
          "data": "2026-05-08",
          "hora": "08:00:00",
          "latitude": -21.85888,
          "longitude": -47.4845167
        },
        "fim": {
          "data": "2026-05-08",
          "hora": "09:10:00",
          "latitude": -21.85922,
          "longitude": -47.48501
        },
        "resumo": {
          "total_pontos": 240,
          "duracao_segundos": 4200,
          "sequencia_ok": true
        },
        "pontos": [
          {
            "latitude": -21.85888,
            "longitude": -47.4845167,
            "ordem": 1
          },
          {
            "latitude": -21.85889,
            "longitude": -47.48453,
            "ordem": 2
          }
        ]
      }
    ]
  }
}

Campos do dispositivo no trajeto

Cada trajeto retorna o bloco dispositivo, indicando qual aparelho registrou aquele trajeto da aplicação.

Esse bloco fica dentro de cada item de trajetos, porque uma aplicação pode ter mais de um trajeto registrado.

5) Como renderizar no app com React Native

O app deve usar os pontos recebidos em data.trajetos[0].pontos para montar a linha do trajeto no Google Maps.

Instalação sugerida

npx expo install react-native-maps

Exemplo simples

import React, { useEffect, useMemo, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import MapView, { Marker, Polyline, PROVIDER_GOOGLE } from 'react-native-maps';

const API_URL = 'https://agroecologia.grupoekos.com.br/router/action.php';

type AplicacaoTrajeto = {
  id_aplicacao: number;
  label: string;
  total_pontos: number;
};

type PontoMapa = {
  latitude: number;
  longitude: number;
  ordem: number;
};
type DispositivoTrajeto = {
  device_id: number;
  apelido: string;
  account_type: string;
};

type TrajetoAplicacao = {
  id_trajeto: number;
  status: string;
  dispositivo: DispositivoTrajeto;
  pontos: PontoMapa[];
};
export default function TrajetosScreen() {
  const [aplicacoes, setAplicacoes] = useState<AplicacaoTrajeto[]>([]);
  const [pontos, setPontos] = useState<PontoMapa[]>([]);

  const APP_KEY = 'APP_KEY_DO_APP';
  const DEVICE_TOKEN = 'DEVICE_TOKEN_SALVO_NO_APARELHO';
  const ID_G10 = 2;

  async function postApi(params: Record<string, string | number>) {
    const body = new FormData();

    Object.entries(params).forEach(([key, value]) => {
      body.append(key, String(value));
    });

    const response = await fetch(API_URL, {
      method: 'POST',
      headers: {
        'X-APP-KEY': APP_KEY,
        'X-DEVICE-TOKEN': DEVICE_TOKEN,
      },
      body,
    });

    const json = await response.json();

    if (!json.success) {
      throw new Error(json.msg || 'Erro ao consultar API.');
    }

    return json.data;
  }

  async function carregarAplicacoes() {
    const data = await postApi({
      action: 'apiG10',
      f: 'listar_aplicacoes_com_trajeto',
      id_g10_participacao: ID_G10,
    });

    setAplicacoes(data.aplicacoes || []);
  }

  async function carregarTrajeto(idAplicacao: number) {
    const data = await postApi({
      action: 'apiG10',
      f: 'listar_trajeto_aplicacao',
      id_aplicacao: idAplicacao,
    });

    const primeiroTrajeto = data.trajetos?.[0] as TrajetoAplicacao | undefined;

    if (!primeiroTrajeto) {
      setPontos([]);
      return;
    }

    setPontos(primeiroTrajeto.pontos || []);
  }

  useEffect(() => {
    carregarAplicacoes();
  }, []);

  const initialRegion = useMemo(() => {
    const primeiro = pontos[0];

    return {
      latitude: primeiro?.latitude || -21.85888,
      longitude: primeiro?.longitude || -47.4845167,
      latitudeDelta: 0.01,
      longitudeDelta: 0.01,
    };
  }, [pontos]);

  return (
    <View style={styles.container}>
      <View style={styles.lista}>
        {aplicacoes.map((app) => (
          <TouchableOpacity
            key={app.id_aplicacao}
            style={styles.botao}
            onPress={() => carregarTrajeto(app.id_aplicacao)}
          >
            <Text style={styles.botaoTexto}>{app.label}</Text>
            <Text style={styles.botaoSub}>{app.total_pontos} pontos</Text>
          </TouchableOpacity>
        ))}
      </View>

      <MapView
        provider={PROVIDER_GOOGLE}
        style={styles.mapa}
        initialRegion={initialRegion}
        mapType="satellite"
      >
        {pontos.length > 0 && (
          <>
            <Polyline
              coordinates={pontos.map((p) => ({
                latitude: p.latitude,
                longitude: p.longitude,
              }))}
              strokeWidth={4}
              strokeColor="#dc2626"
            />

            <Marker
              coordinate={{
                latitude: pontos[0].latitude,
                longitude: pontos[0].longitude,
              }}
              title="Início"
            />

            <Marker
              coordinate={{
                latitude: pontos[pontos.length - 1].latitude,
                longitude: pontos[pontos.length - 1].longitude,
              }}
              title="Fim"
            />
          </>
        )}
      </MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  lista: {
    padding: 12,
    backgroundColor: '#ffffff',
  },
  botao: {
    padding: 12,
    borderRadius: 12,
    backgroundColor: '#f1f5f9',
    marginBottom: 8,
  },
  botaoTexto: {
    fontSize: 15,
    fontWeight: '700',
    color: '#0f172a',
  },
  botaoSub: {
    fontSize: 12,
    color: '#64748b',
    marginTop: 2,
  },
  mapa: {
    flex: 1,
  },
});

6) Observações para os devs

  • Não carregar todos os trajetos de uma vez.
  • Primeiro carregar a lista de aplicações com trajeto.
  • Depois carregar somente o trajeto da aplicação escolhida.
  • Usar Polyline para desenhar o trajeto.
  • Usar marcador no primeiro ponto como início.
  • Usar marcador no último ponto como fim.
  • A linha do trajeto deve ser vermelha.
  • Recurso disponível somente para versão Produtor.
  • Ao carregar o trajeto, usar trajetos[n].dispositivo.apelido para identificar qual aparelho registrou aquele trajeto.

7) Histórico de alterações

  • 01/07/2026: o endpoint listar_aplicacoes_com_trajeto passou a retornar os campos device_id_trajeto, dispositivo_apelido e account_type_trajeto, permitindo ao app identificar qual aparelho realizou o trajeto da aplicação.
  • 01/07/2026: o endpoint listar_trajeto_aplicacao passou a retornar o bloco dispositivo dentro de cada trajeto, contendo device_id, apelido e account_type.