Search code examples
pythonkivykivy-language

Python kivy garden mapview longitude and latitude do not update


I am trying to use a MapView widget from kivy garden, but the position of my marker does not update. I have added a MapView with a MapMarker into the main.kv file. These have the properties lat and lon. I tried to assign them the variables latitude and longitude form the main.py variable by using app.latitude and app.longitude.

Inside my main.py I am using a update function inside the on_start method. The update function simply calls two helper functions that fetches longitude and latitude coordinates (at the moment just random values).

The problem is that my mapview and the marker do not update when I run the code. What am I doing wrong?

# section of main.kv
MapView:
    id: map_view
    zoom: 17
    lat: app.latitude
    lon: app.longitude
    MapMarker:
        id: map_view_marker
        lat: app.latitude
        lon: app.longitude

And here is the section of the main.py

# main.py
… 
class MainApp(App):
…
    # map parameters
    latitude = 50
    longitude = 3

    # Getting latitude and longitude (at the moment just random stuff
    def get_gps_latitude(self):
        self.latitude = self.decimal_precision(0.01 * random.random() + 50.6394, DECIMAL_PRECISION)
        return self.latitude # rounding

    def get_gps_longitude(self):
        self.longitude = self.decimal_precision(0.01 * random.random() + 50.6394, DECIMAL_PRECISION)
        return self.longitude

    def update(self, _):
        self.latitude = self.get_gps_latitude()
        self.longitude = self.get_gps_longitude()

    def on_start(self):
        Clock.schedule_interval(self.update, 1)

Update: Adding the header of my kv file.

#:kivy 1.10.1
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerDivider kivymd.navigationdrawer.NavigationDrawerDivider
#:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar
#:import NavigationDrawerSubheader kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDTextField kivymd.textfields.MDTextField
#:import MDSwitch kivymd.selectioncontrols.MDSwitch
#:import labels application.labels
#:import MapView kivy.garden.mapview
#:import MapMarkerPopup kivy.garden.mapview

NavigationLayout:
    id: nav_layout
    MDNavigationDrawer:
        id: nav_drawer
        NavigationDrawerToolbar:
            title: labels.NAVIGATION
        NavigationDrawerIconButton:
            icon: 'checkbox-blank-circle'
            text: labels.OPERATING_MODE
            on_release: app.root.ids.scr_mngr.current = 'operating_mode'
    BoxLayout:
        orientation: 'vertical'
        halign: "center"
        Toolbar:
            id: toolbar
            title: labels.APPLICATION_NAME
            md_bg_color: app.theme_cls.primary_color
            background_palette: 'Primary'
            background_hue: '500'
            left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
            #right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
        ScreenManager:
            id: scr_mngr
            Screen:
                name: 'operating_mode'
                BoxLayout:
                    orientation: "vertical"
                    padding: dp(48)
                    width: dp(100)
                    spacing: 24
                    BoxLayout:
                        orientation: "horizontal"
                        spacing: 24
                        BoxLayout:
                            orientation: "horizontal"
                            MapView:
                                id: map_view
                                zoom: 10
                                lat: app.latitude
                                lon: app.longitude
                                MapMarkerPopup:
                                    id: map_view_marker
                                    lat: app.latitude
                                    lon: app.longitude

Solution

  • You can only make binding when using Properties, in your case latitude and longitude are not Properties, so they do not generate the change. In this case you must use NumericProperty:

    On the other hand lat and lon of MapView are read-only so the assignment:

    MapView:
        id: map_view
        zoom: 17
        lat: app.latitude  # <---
        lon: app.longitude # <---
    

    set the start value but you can not update it, to update the center of the MapView you must use center_on().

    main.py

    from kivy.app import App
    from kivy.clock import Clock
    from kivy.properties import NumericProperty
    import random
    
    DECIMAL_PRECISION = 2
    
    class MainApp(App):
        # map parameters
        latitude = NumericProperty(50)
        longitude = NumericProperty(3)
    
        def decimal_precision(self, val, precision):
            # foo process
            return val
    
        # Getting latitude and longitude (at the moment just random stuff
        def get_gps_latitude(self):
            self.latitude = self.decimal_precision(0.01 * random.random() + 50.6394, DECIMAL_PRECISION)
            return self.latitude # rounding
    
        def get_gps_longitude(self):
            self.longitude = self.decimal_precision(0.01 * random.random() + 50.6394, DECIMAL_PRECISION)
            return self.longitude
    
        def update(self, _):
            self.latitude = self.get_gps_latitude()
            self.longitude = self.get_gps_longitude()
            self.root.center_on(self.latitude, self.longitude)
    
        def on_start(self):
            Clock.schedule_interval(self.update, 1)
    
    if __name__ == '__main__':
        MainApp().run()
    

    main.kv

    #:import MapView kivy.garden.mapview.MapView
    
    # section of main.kv
    MapView:
        id: map_view
        zoom: 17
        lat: app.latitude
        lon: app.longitude
        MapMarker:
            id: map_view_marker
            lat: app.latitude
            lon: app.longitude
    

    Update:

    map_view is the root child of a very deep hierarchy, so to access it in a simple way a property is created: map_view: map_view, and then we access through the self.root.map_view:

    *.kv

    NavigationLayout:
        id: nav_layout
        map_view: map_view # <---
    

    *.py

    def update(self, _):
        self.latitude = self.get_gps_latitude()
        self.longitude = self.get_gps_longitude()
        self.root.map_view.center_on(self.latitude, self.longitude)