aboutsummaryrefslogtreecommitdiffstats
path: root/tagit/tiles/geo.py
blob: 796a1c20425b4fcf0251f34ac10f5e311e56a7f8 (plain)
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
"""

Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# standard imports
import os

# kivy ipmorts
from kivy.lang import Builder
from kivy.uix.label import Label
import kivy.properties as kp

# tagit imports
# NOTE: the following line segfaults:
#  mapview.source.py:128:self.dp_tile_size = min(dp(self.tile_size), self.tile_size * 2)
# setting it to a static value (e.g. 256) works.
from tagit.external.kivy_garden.mapview import MapView, MapMarkerPopup
from tagit.utils import ns
from tagit.widgets.browser import BrowserAwareMixin

# inner-module imports
from .tile import Tile

# exports
__all__ = ('Map', )


## code ##

Builder.load_string('''
<Map>:
    # meta
    title: "Map"
    tooltip: 'Location of an item'
    # content
    map_: map_
    MapView:
        id: map_
        zoom: 9

<MapLabel>:
    size: self.texture_size
    size_hint: None, None
    padding: 5, 5
    font_size: '15sp'

    canvas.before:
        # background
        Color:
            rgba: 0,0,0,0.6
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: [10] # keep in sync with the line's radius
        # border
        Color:
            rgba: 0,0,0,1
        Line:
            rounded_rectangle: self.x, self.y, self.width, self.height, 10
''')

class MapLabel(Label):
    pass

class Map(Tile, BrowserAwareMixin):
    """Draw a map which indicates visible items' locations."""

    # list of map markers
    markers = kp.ListProperty()

    def on_browser(self, sender, browser):
        """Bind to browser properties."""
        # remove old binding
        if self.browser is not None:
            self.browser.unbind(cursor=self.update)
            self.browser.unbind(items=self.update_markers)
        # add new binding
        self.browser = browser
        if self.browser is not None:
            self.browser.bind(cursor=self.update)
            self.browser.bind(items=self.update_markers)
            # initial calls
            self.update_markers()
            self.update()

    def __del__(self):
        if self.browser is not None:
            self.browser.unbind(cursor=self.update)
            self.browser.unbind(items=self.update_markers)
            self.browser = None

    def update_markers(self, *args):
        """Draw markers for all browser items."""
        # remove old markers
        for mark in self.markers:
            self.map_.remove_marker(mark)
        self.markers.clear()

        # get view data
        data = self.root.browser.unfold(self.root.browser.items).get(
            ns.bse.filename,
            ns.bse.latitude,
            ns.bse.longitude,
            node=True,
            )

        # draw new markers
        for ent, vdict in data.items():
            if ns.bse.latitude not in vdict:
                continue
            if ns.bse.longitude not in vdict:
                continue
            # TODO: cluster points, one marker for multiple items
            lat = vdict[ns.bse.latitude]
            lon = vdict[ns.bse.longitude]
            # create popup marker
            mark = MapMarkerPopup(lat=lat, lon=lon)
            text = vdict.get(ns.bse.filename,
                ', '.join(os.path.basename(guid) for guid in ent.guids))
            mark.add_widget(MapLabel(text=text))
            # add marker
            self.markers.append(mark)
            self.map_.add_marker(mark)

    def update(self, *args):
        """Focus the map on the cursor."""
        if not self.visible:
            return

        cursor = self.root.browser.cursor
        if cursor is not None:
            coords = cursor.get(ns.bse.latitude, ns.bse.longitude)
            if set(coords.keys()) == {ns.bse.latitude, ns.bse.longitude}:
                lat = coords[ns.bse.latitude]
                lon = coords[ns.bse.longitude]
                self.map_.center_on(lat, lon)

## EOF ##