Hello everyone, I am working with python quite a while now and am now faced with the task to program an interactive map visualization over time. I am working at an NGO which supports children in Uganda and we want to visualize all supported children and projects over time on a map of the area (animated). Furthermore some information about the projects (since when, how it helped) and children (age, supported since, etc.) should be shown when hovering or kicking the according dot on ths map. Which libraries and other things do you recommend for this project? Data is stored and edited as a CSV file including coordinates and other important information. Thank you for your help!
Forum Archive
Interactive visualization
RichardSmith
Nov 27, 2019 - 16:33
cvp
Nov 27, 2019 - 16:48
@RichardSmith see an example here
cvp
Nov 27, 2019 - 16:58
Easier way to begin
mikael
Nov 28, 2019 - 05:49
@RichardSmith, I know this might sound odd in our forum, but if this is something you need to work on PCs and maybe send to others etc., I would suggest the map features of Excel.
cvp
Nov 28, 2019 - 08:51
@mikael said:
if this is something you need to work on PCs
In this case, I agree but if he already knows Python and asks it for Pythonista...
cvp
Nov 28, 2019 - 09:45
@RichardSmith Shortest script


import console
from objc_util import *
import ctypes
import ui
class CLLocationCoordinate2D (Structure):
_fields_ = [('latitude', c_double), ('longitude', c_double)]
class MKCoordinateSpan (Structure):
_fields_ = [('d_lat', c_double), ('d_lon', c_double)]
class MKCoordinateRegion (Structure):
_fields_ = [('center', CLLocationCoordinate2D), ('span', MKCoordinateSpan)]
MKPointAnnotation = ObjCClass('MKPointAnnotation')
MKPinAnnotationView = ObjCClass('MKPinAnnotationView')
MKAnnotationView = ObjCClass('MKAnnotationView')
UIColor = ObjCClass('UIColor') # used to set pin color
def mapView_viewForAnnotation_(self,cmd,mk_mapview,mk_annotation):
try:
# not specially called in the same sequence as pins created
# should have one MK(Pin)AnnotationView by type (ex: pin color)
annotation = ObjCInstance(mk_annotation)
mapView = ObjCInstance(mk_mapview)
if annotation.isKindOfClass_(MKPointAnnotation):
tit = str(annotation.title())
subtit = str(annotation.subtitle())
pin_color = annotation.pin_color
id = tit
pinView = mapView.dequeueReusableAnnotationViewWithIdentifier_(id)
if not pinView:
pinView = MKPinAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(annotation,id)
pinView.canShowCallout = True # tap-> show title
pinView.animatesDrop = False # not Animated pin falls like a drop
pinView.pinColor = pin_color # 0=red 1=green 2=purple
else:
pinView.annotation = annotation
return pinView.ptr
return None
except Exception as e:
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
# Build method of MKMapView Delegate
methods = [mapView_viewForAnnotation_]
protocols = ['MKMapViewDelegate']
try:
MyMapViewDelegate = ObjCClass('MyMapViewDelegate')
except Exception as e:
MyMapViewDelegate = create_objc_class('MyMapViewDelegate', methods=methods, protocols=protocols)
#MyMapViewDelegate = create_objc_class('MyMapViewDelegate', NSObject, methods=methods, protocols=protocols)
class MapView(ui.View):
@on_main_thread
def __init__(self, *args, **kwargs):
ui.View.__init__(self, *args, **kwargs)
MKMapView = ObjCClass('MKMapView')
frame = CGRect(CGPoint(0, 0), CGSize(self.width, self.height))
self.mk_map_view = MKMapView.alloc().initWithFrame_(frame)
flex_width, flex_height = (1<<1), (1<<4)
self.mk_map_view.setAutoresizingMask_(flex_width|flex_height)
self_objc = ObjCInstance(self)
self_objc.addSubview_(self.mk_map_view)
self.mk_map_view.release()
# Set Delegate of mk_map_view
map_delegate = MyMapViewDelegate.alloc().init()#.autorelease()
self.mk_map_view.setDelegate_(map_delegate)
@on_main_thread
def add_pin(self, lat, lon, color, title, subtitle):
global all_points
'''Add a pin annotation to the map'''
MKPointAnnotation = ObjCClass('MKPointAnnotation')
coord = CLLocationCoordinate2D(lat, lon)
all_points.append(coord) # store all pin's for MKPolyline
annotation = MKPointAnnotation.alloc().init().autorelease()
annotation.setTitle_(title)
annotation.setSubtitle_(str(subtitle))
annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D])
annotation.pin_color = color
self.mk_map_view.addAnnotation_(annotation)
@on_main_thread
def set_region(self, lat, lon, d_lat, d_lon, animated=False):
'''Set latitude/longitude of the view's center and the zoom level (specified implicitly as a latitude/longitude delta)'''
region = MKCoordinateRegion(CLLocationCoordinate2D(lat, lon), MKCoordinateSpan(d_lat, d_lon))
self.mk_map_view.setRegion_animated_(region, animated, restype=None, argtypes=[MKCoordinateRegion, c_bool])
def main():
global all_points
#----- Main process -----
console.clear()
# Hide script
back = MapView()
back.background_color='white'
back.name = 'Test for @RichardSmith'
back.present('fullscreen', hide_title_bar=False)
map_points = []
# assume infos come from a CSV file
map_points.append((0.5830414, 32.5316170, 0, 'Yoweri Museveni', '12 years, supported since 01/02/2019'))
map_points.append((0.2986261, 32.6033583, 1, "The Uganda National N.G.O Forum", 'since 1997, Providing a Sharing and Reflection Platform for NGOs in Uganda'))
# min and max of latitude and longitude
min_lat = min(map_points,key = lambda x:x[0])[0]
max_lat = max(map_points,key = lambda x:x[0])[0]
min_lon = min(map_points,key = lambda x:x[1])[1]
max_lon = max(map_points,key = lambda x:x[1])[1]
# Display map, center and zoom so all points are visible
back.set_region((min_lat+max_lat)/2,(min_lon+max_lon)/2, 4*(max_lat-min_lat), 4*(max_lon-min_lon), animated=True)
# Display pin's
all_points = []
for point in map_points:
back.add_pin(point[0],point[1],point[2],point[3],point[4])
# Protect against import
if __name__ == '__main__':
main()
cvp
Nov 28, 2019 - 11:03
Add these lines if you want multiple lines in the subtitle
.
.
.
pinView.pinColor = pin_color # 0=red 1=green 2=purple
# add these lines if you want several lines in subtitle: begin
l_title = ui.Label()
l_title.font = ('Menlo',12) # police echappement fixe
lo = ObjCInstance(l_title)
l_title.text = subtit.replace(',','\n')
lo.setNumberOfLines_(0)
pinView.setDetailCalloutAccessoryView_(lo)
# add these lines if you want several lines in subtitle: end
