Created
April 28, 2022 06:20
-
-
Save FilipeMarch/b544ca1b260e36e3a5045738492aeb50 to your computer and use it in GitHub Desktop.
How to draw driving routes on MapView on Kivy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from kivy.lang import Builder | |
from kivy.factory import Factory as F | |
from kivymd.app import MDApp | |
from kivy_garden.mapview.source import MapSource | |
from kivy_garden.mapview import MapView, MapMarkerPopup | |
from kivy.graphics.vertex_instructions import Line | |
from kivy.animation import Animation | |
from kivy.graphics import Color | |
from kivy.clock import Clock | |
from mapbox import Directions | |
Builder.load_string(""" | |
<MapViewScreen>: | |
map_view: map_view.__self__ | |
animated_street_path: animated_street_path.__self__ | |
BoxLayout: | |
id: main_box | |
orientation: 'vertical' | |
padding: dp(15) | |
spacing: dp(15) | |
MDLabel: | |
text: '[b]Animated Street Path' | |
markup: True | |
halign: 'center' | |
valign: 'center' | |
font_size: sp(22) | |
size_hint_y: None | |
height: self.texture_size[1] | |
MapView: | |
id: map_view | |
zoom:15 | |
lat: (45.5801+45.5831)/2 | |
lon: (-122.7282-122.7482)/2 | |
map_source: root.source | |
BoxLayout: | |
spacing: dp(10) | |
size_hint_y: None | |
height: self.minimum_height + dp(10) | |
Widget: | |
MDFillRoundFlatButton: | |
text: 'Add Marker 1' | |
pos_hint: {'center_y': .5} | |
font_size: sp(18) | |
on_release: app.add_marker() | |
Widget: | |
MDFillRoundFlatButton: | |
text: 'Add Marker 2' | |
pos_hint: {'center_y': .5} | |
font_size: sp(18) | |
on_release: app.add_another_marker() | |
Widget: | |
MDFillRoundFlatButton: | |
text: 'Show Path Between Points' | |
pos_hint: {'center_y': .5} | |
font_size: sp(18) | |
on_release: app.show_path_between_points() | |
Widget: | |
AnimatedStreetPath: | |
id: animated_street_path | |
<AnimatedStreetPath>: | |
points: [] | |
canvas: | |
Color: | |
rgba: 1,1,1,1 | |
Line: | |
points: self.points | |
width: 2 | |
""") | |
class AnimatedStreetPath(F.FloatLayout): | |
def start_animation(self, points) -> None: | |
# Initial position of the line | |
self.points = [300 for point in points] | |
# Updating its position | |
anim = Animation(points=points, duration=2, t='out_sine') | |
anim.start(self) | |
class MapViewScreen(F.Screen): | |
source = MapSource( | |
url="https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.jpg?access_token=pk.YOUR_ACCESS_TOKEN", | |
cache_key="my-custom-map", | |
tile_size=256, | |
min_zoom=6, | |
max_zoom=17 | |
) | |
class MainApp(MDApp): | |
def build(self): | |
self.screen = MapViewScreen() | |
self.map_view = self.screen.map_view | |
return self.screen | |
def on_start(self) -> None: | |
""" | |
When the screen is entered, the map is animated | |
""" | |
Animation(opacity=1, duration=1).start(self.map_view) | |
def add_marker(self) -> None: | |
""" | |
Adds a marker to the map | |
""" | |
self.current_marker = MapMarkerPopup( | |
lat=45.5801, | |
lon=-122.7282, | |
source='data/images/location-pin.png' | |
) | |
self.map_view.add_marker(self.current_marker) | |
def add_another_marker(self) -> None: | |
""" | |
Adds another marker to the map | |
""" | |
self.current_marker2 = MapMarkerPopup( | |
lat=45.5831, | |
lon=-122.7482, | |
source='data/images/location-pin.png' | |
) | |
self.map_view.add_marker(self.current_marker2) | |
def show_path_between_points(self) -> None: | |
""" | |
Shows a path between two markers by adding | |
consecutive lines on the map. The path is then animated. | |
""" | |
# Create the Directions object from MapBox | |
service = Directions( | |
access_token="pk.YOUR_ACCESS_TOKEN") | |
origin = { | |
'type': 'Feature', | |
'properties': {'name': 'Portland, OR'}, | |
'geometry': { | |
'type': 'Point', | |
'coordinates': [-122.7282, 45.5801]}} | |
destination = { | |
'type': 'Feature', | |
'properties': {'name': 'Portland, OR'}, | |
'geometry': { | |
'type': 'Point', | |
'coordinates': [-122.7482, 45.5831]}} | |
# Get the route between the two points | |
response = service.directions([origin, destination], 'mapbox.driving') | |
driving_routes = response.geojson() | |
# Get the coordinates of the route | |
coordinates = driving_routes['features'][0]['geometry']['coordinates'] | |
# Create the list of points | |
points = self.get_windows_points(coordinates) | |
# Start the animation | |
self.screen.animated_street_path.start_animation(points) | |
def get_windows_points(self, coordinates) -> list: | |
""" | |
Gets the points to be used in the animation | |
Parameters: | |
coordinates: List of coordinates | |
Returns: | |
List of points | |
""" | |
points = [] | |
for coordinate in coordinates: | |
x, y = self.map_view.get_window_xy_from( | |
coordinate[1], coordinate[0], self.map_view.zoom) | |
points.append(x) | |
points.append(y) | |
return points | |
MainApp().run() |
Author
FilipeMarch
commented
Apr 28, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment