Forum Archive

Mapview help

shaun-h

If anyone could help that would be great I am trying to implement a mkmapviewdelegate to change the way pins look, now I have based this of @omz map view demo from a while ago.

Here is the code

# coding: utf-8
# This has been derived for mapview example by Ole Zorn @omz url to come soon
import ui
import location
from objc_util import *

MKUserLocation = ObjCClass('MKUserLocation')
MKAnnotationView = ObjCClass('MKPinAnnotationView')
MKPointAnnotation = ObjCClass('MKPointAnnotation')
MKPinAnnotationView = ObjCClass('MKPinAnnotationView')

UIColor = ObjCClass('UIColor')
def mapView_viewForAnnotation_(self, cmd, mk_mapview, annotation):
    try:
        anno = ObjCInstance(annotation)
        mapView = ObjCInstance(mk_mapview)
        if anno.isKindOfClass_(MKPointAnnotation):
            pinView = mapView.dequeueReusableAnnotationViewWithIdentifier_('annoview')
            if not pinView:
                pinView = MKPinAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview')
                pinView.canShowCallout = False
            else:
                pinView.annotation = anno
            return pinView
        return None
    except Exception as e:
        print e

methods = [mapView_viewForAnnotation_]
protocols = ['MKMapViewDelegate']
try:
    MyMapViewDelegate = ObjCClass('MyMapViewDelegate')
except:
    MyMapViewDelegate = create_objc_class('MyMapViewDelegate', NSObject, methods=methods, protocols=protocols)



class CLLocationCoordinate2D (Structure):
    _fields_ = [('latitude', c_double), ('longitude', c_double)]

class MapView (ui.View):
    @on_main_thread
    def __init__(self, *args, **kwargs):
        try:
            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()
            self.map_delegate = MyMapViewDelegate.alloc().init().autorelease()
            self.mk_map_view.setDelegate_(self.map_delegate)
        except Exception as e:
            print e

    @on_main_thread
    def add_pin(self, lat, lon, title, subtitle=None, select=False):
        '''Add a pin annotation to the map'''
        MKPointAnnotation = ObjCClass('MKPointAnnotation')
        coord = CLLocationCoordinate2D(lat, lon)
        annotation = MKPointAnnotation.alloc().init().autorelease()
        annotation.setTitle_(title)
        if subtitle:
            annotation.setSubtitle_(subtitle)
        annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D])
        self.mk_map_view.addAnnotation_(annotation)
        if select:
            self.mk_map_view.selectAnnotation_animated_(annotation, True)

    @on_main_thread
    def remove_all_pins(self):
        '''Remove all annotations (pins) from the map'''
        self.mk_map_view.removeAnnotations_(self.mk_map_view.annotations())

if __name__ == '__main__':
    m = MapView()
    location.start_updates()
    loc = location.get_location()
    location.stop_updates()
    m.add_pin(lat = loc['latitude'], lon = loc['longitude'],title='Test')
    m.mk_map_view.setShowsUserLocation_(False)
    m.present()

If I change return pinView to return None in mapView_viewForAnnotation_ then it doesn't crash and works happily, any advice would be great.

Thanks

brumm
            if not pinView:
                pinView = MKPinAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview')
                pinView.canShowCallout = False
                return None
            else:
                pinView.annotation = anno
                return pinView

Don't know if this makes sense (because I don't know anything about objc_util).

shaun-h

Returning None will show the standard annotation view which I don't want to do, as referenced here

It seems to crash when it tries to use the annotation view that I create, well that is what I think is the problem anyway.

JonB

Have you tried retain_global(pinView)? That would fix a problem if it is falling out of scope.
Alternatively, you might need to return pinView.ptr. I forget how callbacks work in objc, and whether that conversion is automatic.

omz

Yes, you need to return raw pointers from ObjC callbacks (as @JonB suggested), returning an ObjCInstance won't work.

shaun-h

Thanks, @JonB & @omz the pinView.ptr seems to be working, I appreciate the help.

shaun-h

I have ran in to another problem now. I have changed the code to try and use my own image for the mkannotationview see below

# coding: utf-8
# This has been derived for mapview example by Ole Zorn @omz url to come soon
import ui
import location
from objc_util import *

MKUserLocation = ObjCClass('MKUserLocation')
MKAnnotationView = ObjCClass('MKAnnotationView')
MKPointAnnotation = ObjCClass('MKPointAnnotation')
MKPinAnnotationView = ObjCClass('MKPinAnnotationView')

UIColor = ObjCClass('UIColor')
UIImage = ObjCClass('UIImage')
def mapView_viewForAnnotation_(self, cmd, mk_mapview, annotation):
    try:
        anno = ObjCInstance(annotation)
        mapView = ObjCInstance(mk_mapview)
        if anno.isKindOfClass_(MKPointAnnotation):
            pinView = mapView.dequeueReusableAnnotationViewWithIdentifier_('annoview')
            if not pinView:
                pinView = MKAnnotationView.alloc().initWithAnnotation_reuseIdentifier_(anno, 'annoview')
                pinView.canShowCallout = False
                pinView.image = ui.Image.named('iob:alert_24')
            else:
                pinView.annotation = anno
            return pinView.ptr
        return None
    except Exception as e:
        print 'exception: '
        print e

methods = [mapView_viewForAnnotation_]
protocols = ['MKMapViewDelegate']
try:
    MyMapViewDelegate = ObjCClass('MyMapViewDelegate')
except:
    MyMapViewDelegate = create_objc_class('MyMapViewDelegate', NSObject, methods=methods, protocols=protocols)



class CLLocationCoordinate2D (Structure):
    _fields_ = [('latitude', c_double), ('longitude', c_double)]

class MapView (ui.View):
    @on_main_thread
    def __init__(self, *args, **kwargs):
        try:
            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()
            self.map_delegate = MyMapViewDelegate.alloc().init().autorelease()
            self.mk_map_view.setDelegate_(self.map_delegate)
        except Exception as e:
            print e

    @on_main_thread
    def add_pin(self, lat, lon, title, subtitle=None, select=False):
        '''Add a pin annotation to the map'''
        MKPointAnnotation = ObjCClass('MKPointAnnotation')
        coord = CLLocationCoordinate2D(lat, lon)
        annotation = MKPointAnnotation.alloc().init().autorelease()
        annotation.setTitle_(title)
        if subtitle:
            annotation.setSubtitle_(subtitle)
        annotation.setCoordinate_(coord, restype=None, argtypes=[CLLocationCoordinate2D])
        self.mk_map_view.addAnnotation_(annotation)
        if select:
            self.mk_map_view.selectAnnotation_animated_(annotation, True)

    @on_main_thread
    def remove_all_pins(self):
        '''Remove all annotations (pins) from the map'''
        self.mk_map_view.removeAnnotations_(self.mk_map_view.annotations())

if __name__ == '__main__':
    m = MapView()
    location.start_updates()
    loc = location.get_location()
    location.stop_updates()
    m.add_pin(lat = loc['latitude'], lon = loc['longitude'],title='Test')
    m.mk_map_view.setShowsUserLocation_(False)
    m.present()

I get this exception thrown

exception:
Expected "}" (at char 9), (line:1, col:10)

Any help would be appreciated again

omz

This seems to be a bug in the objc_util module. For some reason, the type encoding for the setImage: method is not what I'd expect, and objc_util isn't able to parse it correctly.

I'll have to look into this in more depth, but here's a quick workaround:

# Instead of pinView.image = ... use:
setImage = pinView.setImage_
setImage.encoding = 'v24@0:8@16'
setImage(ui.Image.named('iob:alert_24'))
shaun-h

Thanks @omz that's works a treat, if you didn't know or guessed I tried it pythonista 3 and the same issue exists.