0

I'm creating a tracker for my car, and one of the features I want to add is the vehicle's movement history. On the history screen, I retrieve the positions from the Firebase Realtime Database and I'm able to add markers on the map, but that's not what I need. What I want is to display a line connecting the positions.

I've searched a lot on Google, watched all sorts of tutorials on YouTube, but I couldn't adapt the solutions I found to work with my code (I'm a beginner with Kivy, and this is my first experience with it).

In my search for solutions to this problem, I came across some examples where markers are added and a line is drawn to connect them, but besides not being able to make it work in my code, it's not what I need. I would like only the initial and final markers to appear, and the rest of the route to be displayed as a line on the screen.

This is the best I could do so far:

After selecting the date you want to view the history, this is the screen that is displayed.

Can anyone tell me the best way to do this?

Below is the Python code and the KV code for you to take a look.

Thanks.

This is the Python code:

from kivy.uix.screenmanager import ScreenManager, Screen
from kivy_garden.mapview import MapView, MapMarkerPopup, MapMarker
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.pickers import MDDatePicker
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.graphics import Color, Line
from firebase import FireBase
from mqtt import Mqtt


class NetBeast(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Green"
        return Builder.load_file("main.kv")


class Login(Screen):
    def __init__(self, **kwargs):
        super(Login, self).__init__(**kwargs)
        self.auth = FireBase()
        self.dialog = None
        self.mqtt = Mqtt()

    def logar(self):
        if self.ids.user.text == "" or self.ids.password.text == "":
            self.alerta("Os campos de 'usuário' e 'senha' devem ser preenchidos!")
        else:
            if self.auth.login(self.ids.user.text, self.ids.password.text):
                self.iniciar()
                self.mqtt.conecta_mqtt()
            else:
                self.alerta("Usuário ou senha inválidos:")


    @staticmethod
    def iniciar():
        NetBeast.get_running_app().root.current = 'inicial'

    def clear(self):
        self.ids.user.text = ""
        self.ids.password.text = ""

    def alerta(self, mensagem):
        self.dialog = MDDialog(
            text=mensagem,
            buttons=[
                MDFlatButton(
                    text="OK",
                    text_color=(0, 0, 0, 1),
                    md_bg_color=(1, 1, 1, 1),
                    on_release=self.fecha_alerta)])
        self.dialog.open()

    def fecha_alerta(self, obj):
        self.dialog.dismiss()
        self.dialog = None

    def esqueci(self):
        if self.ids.user.text == '':
            self.alerta('Preencha o campo de e-mail para que a mensagem possa ser enviada.')
        else:
            self.auth.reset_pwd(self.ids.user.text)
            self.alerta(
                'Se o e-mail preenchido estiver correto, em breve você receberá '
                'uma mensagem com o link para cadastrar uma nova senha.')


class Inicial(Screen):
    def __init__(self, **kwargs):
        super(Inicial, self).__init__(**kwargs)
        Clock.schedule_interval(self.atualizar_botoes, 3)
        self.alarme = False
        self.roubado = False
        self.reinicia = False
        self.mqtt = Mqtt()

    def atualizar_botoes(self, arg1 = None, arg2 = None):
        if self.mqtt.alarme_est == 1:
            self.ids.alarme_btn.md_bg_color = (1, 78/255, 78/255, 1)
            self.alarme = True
        else:
            self.ids.alarme_btn.md_bg_color = (5/255, 92/255, 54/255, 1)
            self.alarme = False

        if self.mqtt.roubado_est == 1:
            self.ids.roubado_btn.md_bg_color = (1, 78/255, 78/255, 1)
            self.roubado = True
            self.ids.alarme_btn.md_bg_color = (1, 78 / 255, 78 / 255, 1)
            self.alarme = True
        else:
            self.ids.roubado_btn.md_bg_color = (5/255, 92/255, 54/255, 1)
            self.roubado = False

        if self.mqtt.reinicia_est == 1:
            self.ids.reinicia_btn.md_bg_color = (1, 78/255, 78/255, 1)
            self.reinicia = True
        else:
            self.ids.reinicia_btn.md_bg_color = (5/255, 92/255, 54/255, 1)
            self.reinicia = False

    def alarme_press(self):
        if not self.roubado:
            if self.alarme:
                self.ids.alarme_btn.md_bg_color = (5 / 255, 92 / 255, 54 / 255, 0.4)
                self.mqtt.envia_dados('mymqtt/alarme', '0')
                self.alarme = False
            else:
                self.ids.alarme_btn.md_bg_color = (1, 78 / 255, 78 / 255, 0.4)
                self.mqtt.envia_dados('mymqtt/alarme', '1')
                self.alarme = True

    def roubado_press(self):
        if self.roubado:
            self.ids.roubado_btn.md_bg_color = (5 / 255, 92 / 255, 54 / 255, 0.4)
            self.mqtt.envia_dados('mymqtt/roubado', '0')
            self.roubado = False
        else:
            self.ids.roubado_btn.md_bg_color = (1, 78 / 255, 78 / 255, 0.4)
            self.ids.alarme_btn.md_bg_color = (1, 78 / 255, 78 / 255, 0.4)
            self.mqtt.envia_dados('mymqtt/roubado', '1')
            self.roubado = True
            self.alarme = True

    def reinicia_press(self):
        if self.reinicia:
            self.ids.reinicia_btn.md_bg_color = (5 / 255, 92 / 255, 54 / 255, 0.4)
            self.mqtt.envia_dados('mymqtt/reinicia', '0')
            self.reinicia = False
        else:
            self.ids.reinicia_btn.md_bg_color = (1, 78 / 255, 78 / 255, 0.4)
            self.mqtt.envia_dados('mymqtt/reinicia', '1')
            self.reinicia = True

    def fechar(self):
        self.mqtt.desconectar()
        quit()


class Mapa(Screen):
    def __init__(self, **kwargs):
        super(Mapa, self).__init__(**kwargs)
        self.existe = False
        Clock.schedule_interval(self.add_marker, 3)
        self.mqtt = Mqtt()
        self.marker = None
        self.marker_old = None
        self.inicio = True

    def add_marker(self, arg1 = None, arg2 = None):
        if self.mqtt.chegou:
            if self.inicio:
                self.ids.mapview.zoom = 15
                self.inicio = False
            gps = self.mqtt.get_gps
            self.marker = MapMarkerPopup(lat=gps[0], lon=gps[1], source='Variant_MapP.png')
            if self.existe:
                self.ids.mapview.remove_widget(self.marker_old)
            self.ids.mapview.add_widget(self.marker)
            self.ids.mapview.center_on(float(gps[0]), float(gps[1]))
            self.ids.velocidade.text = f'{gps[2]} Km/h'
            self.marker_old = self.marker
            self.existe = True


# This is the class responsible for the history.
class Historico(Screen):
    def __init__(self, **kwargs):
        super(Historico, self).__init__(**kwargs)
        self.realtime = FireBase()
        self.locais = None
        #J ust for testing purposes, I'm using this variable in place of the actual data coming from Firebase.
        self.pontos = [[-23.5609874, -46.6841326], [-23.5600753, -46.6844432], [-23.5608877, -46.6854705]]
        self.dialog = None

    def seleciona_data(self):
        date_dialog = MDDatePicker()
        date_dialog.bind(on_save=self.on_save, on_cancel=self.on_cancel)
        date_dialog.open()

    def on_save(self, instance, value, date_range):
        marcadores = []
        lat_lon = []
        for i in range(len(self.pontos)):
            print(i)
            lat, lon = self.pontos[i]
            print(lat, lon)
            marcadores.append(MapMarker(lat=lat, lon=lon))
            self.ids.maphist.add_widget(marcadores[i])
        self.ids.maphist.lat = self.pontos[0][0]
        self.ids.maphist.lon = self.pontos[0][1]
        self.ids.maphist.zoom = 17
        for marcador in marcadores:
            lat_lon.append(self.ids.maphist.get_window_xy_from(marcador.lon, marcador.lat, 17))
        with self.ids.maphist.canvas:
            Color(1, 0, 0)
            Line(points=lat_lon, width=2)

    def on_cancel(self, instance, value):
        pass

    def fecha_alerta(self, obj):
        self.dialog.dismiss()
        self.dialog = None


class Gerenciador(ScreenManager):
    def __init__(self, **kwargs):
        super(Gerenciador, self).__init__(**kwargs)

    @staticmethod
    def mudar():
        ScreenManager.current = 'iniciar'

This is the KV code:

Gerenciador:
    Login:
    Inicial:
    Mapa:
    Historico:

<Login>:
    id: tela_login
    name: "login"
    md_bg_color: 1, 1, 1, 1
    MDBoxLayout:
        orientation: "vertical"
        md_bg_color: 1, 1, 1, 1
        spacing: 40
        padding: 15
        MDLabel:
            id: nblogin
            text: "NetBeast Tracker"
            font_size: 25
            color:  5/255, 92/255, 54/255, 1
            halign: 'center'
            size_hint_y: None
            height: self.texture_size[1]
            padding_y: 15

        MDTextField:
            id: user
            hint_text: 'usuário'
            icon_right: 'account'
            size_hint_x: None
            width: 200
            font_size: 18
            text_color: 0, 0, 0, 1
            pos_hint: {'center_x': 0.5, 'center_y': 0.5}

        MDTextField:
            id: password
            hint_text: 'senha'
            icon_right: 'key-variant'
            size_hint_x: None
            width: 200
            font_size: 18
            pos_hint: {'center_x': 0.5, 'center_y': 0.5}
            password: True

        MDGridLayout:
            cols: 3
            MDAnchorLayout:
                anchor_x: 'right'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    text: 'Login'
                    text_color: 1, 1, 1, 1
                    md_bg_color: 5/255, 92/255, 54/255, 1
                    font_size: 12
                    on_release: root.logar()
            MDAnchorLayout:
                anchor_x: 'center'
                anchor_y: 'bottom'
                Widget:
                    width: 20
            MDAnchorLayout:
                anchor_x: 'left'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    text: 'Limpar'
                    text_color: 1, 1, 1, 1
                    md_bg_color: 5/255, 92/255, 54/255, 1
                    font_size: 12
                    on_release: root.clear()
        MDRoundFlatButton:
            pos_hint: {"center_x": 0.5, "center_y": 0.5}
            text: 'Esqueci a senha'
            font_size: 12
            text_color: 1, 1, 1, 1
            md_bg_color: 5/255, 92/255, 54/255, 1
            on_release: root.esqueci()

<Inicial>:
    id: tela_inicial
    name: "inicial"
    MDBoxLayout:
        orientation: "vertical"
        md_bg_color: 1, 1, 1, 1
        spacing: 80
        padding: 30
        MDRoundFlatButton:
            id: alarme_btn
            text: "Alarme"
            font_size: 18
            text_color: 1, 1, 1, 1
            md_bg_color: 5/255, 92/255, 54/255, 1
            size_hint: 1, 0.1
            pos_hint: {"center_x": 0.5}
            on_release: root.alarme_press()
        MDRoundFlatButton:
            id: roubado_btn
            text: "Seguro/Roubado"
            font_size: 18
            text_color: 1, 1, 1, 1
            md_bg_color: 5/255, 92/255, 54/255, 1
            size_hint: 1, 0.1
            pos_hint: {"center_x": 0.5}
            on_release: root.roubado_press()
        MDRoundFlatButton:
            text: "Ver Mapa"
            font_size: 18
            text_color: 1, 1, 1, 1
            md_bg_color: 5/255, 92/255, 54/255, 1
            size_hint: 1, 0.1
            pos_hint: {"center_x": 0.5}
            on_release:
                app.root.current = "mapa"
                root.manager.transition.direction = "left"
        MDRoundFlatButton:
            id: reinicia_btn
            text: "Reiniciar Dispositivo"
            font_size: 18
            text_color: 1, 1, 1, 1
            md_bg_color: 5/255, 92/255, 54/255, 1
            size_hint: 1, 0.1
            pos_hint: {"center_x": 0.5}
            on_release: root.reinicia_press()
        MDRoundFlatButton:
            text: "Fechar App"
            font_size: 18
            font_name: "Roboto"
            text_color: 1, 1, 1, 1
            md_bg_color: 1, 78/255, 78/255, 1
            size_hint: 0.7, 0.1
            pos_hint: {"center_x": 0.5}
            on_release: root.fechar()


<Mapa>:
    id: tela_mapa
    name: "mapa"
    MDBoxLayout:
        id: box_mapa
        spacing: 10
        padding: 5
        md_bg_color: 1, 1, 1, 1
        orientation: 'vertical'

        MapView:
            id: mapview
            lat: -14.9074614
            lon: -54.5827372
            zoom: 3

        MDGridLayout:
            size_hint: 0.9, 0.06
            cols: 3

            MDAnchorLayout:
                anchor_x: 'right'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    id: voltar_btn
                    text: "Voltar"
                    font_size: 14
                    #text_color: 0, 0, 1, 1
                    #md_bg_color: 158/255, 158/255, 158/255, 1
                    font_name: "Roboto"
                    size_hint: 0.3, 0.1
                    pos_hint: {"x": 0, "y": 0}
                    on_release:
                        app.root.current = "inicial"
                        root.manager.transition.direction = "right"

            MDAnchorLayout:
                anchor_x: 'center'
                anchor_y: 'bottom'

                MDLabel:
                    id: velocidade
                    text: ""
                    font_name: "Roboto"
                    font_size: 14
                    halign: 'center'
                    size_hint_y: None
                    height: self.texture_size[1]
                    padding_y: 8
                #Widget:
                #    width: 20

            MDAnchorLayout:
                anchor_x: 'left'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    id: historico_btn
                    text: "Historico"
                    font_size: 14
                    #text_color: 1, 1, 1, 1
                    #md_bg_color: 158/255, 158/255, 158/255, 1
                    font_name: "Roboto"
                    size_hint: 0.3, 0.1
                    pos_hint: {"x": 0, "y": 0}
                    on_release:
                        app.root.current = "historico"
                        root.manager.transition.direction = "left"

# Here is the part responsible for displaying the history.
<Historico>:
    id: tela_historico
    name: "historico"
    MDBoxLayout:
        spacing: 10
        padding: 5
        md_bg_color: 1, 1, 1, 1
        orientation: 'vertical'
        MapView:
            id: maphist
            lat: -14.9074614
            lon: -54.5827372
            zoom: 3
        MDGridLayout:
            size_hint: 0.8, 0.06
            cols: 3
            MDAnchorLayout:
                anchor_x: 'right'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    id: voltar_hist_btn
                    text: "Voltar"
                    font_size: 14
                    #text_color: 1, 1, 1, 1
                    #md_bg_color: 158/255, 158/255, 158/255, 1
                    font_name: "Roboto"
                    size_hint: 0.3, 0.1
                    pos_hint: {"x": 0, "y": 1}
                    on_release:
                        app.root.current = "mapa"
                        root.manager.transition.direction = "right"
            MDAnchorLayout:
                anchor_x: 'center'
                anchor_y: 'bottom'
                Widget:

            MDAnchorLayout:
                anchor_x: 'left'
                anchor_y: 'bottom'
                MDRoundFlatButton:
                    id: data_btn
                    text: "Selecione a data"
                    font_size: 14
                    #text_color: 1, 1, 1, 1
                    #md_bg_color: 158/255, 158/255, 158/255, 1
                    font_name: "Roboto"
                    size_hint: 0.3, 0.06
                    pos_hint: {"x": 0, "y": 0}
                    on_release: root.seleciona_data()

0 Answers0