Skip to content

Instantly share code, notes, and snippets.

@FilipeMarch
Created April 28, 2022 06:20
Show Gist options
  • Save FilipeMarch/b544ca1b260e36e3a5045738492aeb50 to your computer and use it in GitHub Desktop.
Save FilipeMarch/b544ca1b260e36e3a5045738492aeb50 to your computer and use it in GitHub Desktop.
How to draw driving routes on MapView on Kivy
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()
@FilipeMarch
Copy link
Author

ezgif com-gif-maker (1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment