Skip to content

Instantly share code, notes, and snippets.

@martinbowling
Forked from Clancey/gist:1047006
Created September 21, 2011 20:17
Show Gist options
  • Save martinbowling/1233161 to your computer and use it in GitHub Desktop.
Save martinbowling/1233161 to your computer and use it in GitHub Desktop.
MapView With MKPolyLines For route
using System;
using MonoTouch.UIKit;
using System.Collections.Generic;
using MonoTouch.MapKit;
using System.Threading;
using ClanceysLib;
using MonoTouch.Foundation;
using System.Linq;
using MonoTouch.CoreLocation;
using System.Drawing;
using MapStuff.DrawMap;
using MonoTouch.ObjCRuntime;
using System.Xml.Linq;
using MonoTouch.CoreGraphics;
namespace ParkApp
{
public class MapViewController : UIViewController
{
public List<MKPolyline> roads = new List<MKPolyline> ();
public Park Park;
public MKMapView map;
public UISegmentedControl switcher;
public MBProgressHUD progress;
public MKMapRect maxRect;
public List<PersonAnnotation> People;
public bool LockMap = false;
public PartySettingsDvc partySettingsDvc;
public MapViewController (Park park) : base()
{
People = new List<PersonAnnotation> ();
Park = park;
map = new MKMapView ();
map.Frame = this.View.Bounds;
map.MapType = MKMapType.Hybrid;
map.Region = getMapBounds ();
//map.SetVisibleMapRect(new MKMapRect(46286327.3006886 , 107394439.844582,12713.8773526251 , 8475.9182350859),false);
maxRect = map.visibleMapRect;
map.Delegate = new MapViewDelegate (this);
map.ShowsUserLocation = true;
switcher = new UISegmentedControl (new string[] { "Rides", "Food", "Restrooms" });
switcher.Frame = new RectangleF (35, 0, 250, 25);
switcher.ControlStyle = UISegmentedControlStyle.Bar;
switcher.SelectedSegment = 0;
switcher.ValueChanged += delegate { ShowAnnotations (); };
this.NavigationItem.RightBarButtonItem = new UIBarButtonItem ("Party", UIBarButtonItemStyle.Plain, delegate {
partySettingsDvc = new PartySettingsDvc();
this.NavigationController.PushViewController(partySettingsDvc,true);
});
this.NavigationItem.TitleView = switcher;
this.View.AddSubview (map);
progress = new MBProgressHUD (UIApplication.SharedApplication.KeyWindow);
Thread thread = new Thread (LoadPaths);
thread.Start ();
//AddPaths();
//AddWays();
//this.Add(new OptionsView());
}
public override void ViewDidAppear (bool animated)
{
//this.NavigationItem.TitleView.AddSubview(switcher);
base.ViewDidAppear (animated);
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public void UpdatePeopleLocations (ParkHopperLibrary.Models.PartyMember[] people)
{
map.RemoveAnnotations (People.ToArray ());
People.Clear ();
foreach (var person in people)
{
var ann = new PersonAnnotation (person);
People.Add (ann);
map.AddAnnotation (ann);
}
}
public MKCoordinateRegion getMapBounds ()
{
return new MKCoordinateRegion (new CLLocationCoordinate2D (33.8092652025279, -117.919469247912), new MKCoordinateSpan (0.0135665781450101, 0.0113583849480818));
}
bool hasAnnotations;
public void ShowAnnotations ()
{
Console.WriteLine ("start");
if (hasAnnotations)
{
map.RemoveAnnotations (map.Annotations.Where (x => x is MyAnnotation || x is RestaurantAnnotation || x is RestroomAnnotation).Select (x => (MKAnnotation)x).ToArray ());
hasAnnotations = false;
}
Console.WriteLine ("past the linq");
Thread thread = new Thread (getAnnotations);
thread.Start ();
}
private void getAnnotations ()
{
using (new NSAutoreleasePool ())
{
if (switcher.SelectedSegment == 0)
AddAttactions ();
else if (switcher.SelectedSegment == 1)
AddFood();
else
{
AddRestrooms();
}
}
}
private void AddFood()
{
Console.WriteLine("Add Restaurants");
var restaurants = Database.Main.Table<Restaurant>().Where(x=> x.Park == Park).ToArray();
foreach( var restaurant in restaurants)
{
this.InvokeOnMainThread( delegate {
map.AddAnnotation(new RestaurantAnnotation(restaurant));
});
hasAnnotations = true;
}
}
private void AddRestrooms()
{
Console.WriteLine("Add Restrooms");
var restroooms = Database.Main.Table<Restroom>().Where(x=> x.Park == Park).ToArray();
foreach( var restroom in restroooms)
{
this.InvokeOnMainThread( delegate {
map.AddAnnotation(new RestroomAnnotation(restroom));
});
hasAnnotations = true;
}
}
public void AddAttactions ()
{
Console.WriteLine ("Add Attractions");
var attractions = Database.Main.Table<Tag> ().Where (x => x.k == "attraction" && x.Park == Park).Select (y => y.nodeId).ToArray ();
foreach (var attraction in attractions)
{
var name = Database.Main.Table<Tag> ().Where (x => x.nodeId == attraction && x.k == "name").Select (y => y.v).FirstOrDefault () ?? "";
var type = Database.Main.Table<Tag> ().Where (x => x.nodeId == attraction && x.k == "attraction").Select (y => y.v).FirstOrDefault () ?? "";
var node = Database.Main.Table<Node> ().Where (x => x.id == attraction).FirstOrDefault ();
this.BeginInvokeOnMainThread (delegate { map.AddAnnotation (new MyAnnotation (new CLLocationCoordinate2D (node.lat, node.lon), name, type, node.id)); });
hasAnnotations = true;
}
}
public void LoadPaths ()
{
using (new NSAutoreleasePool ())
{
progress.Show (true);
//AddWays();
addWaysDatabase ();
ShowAnnotations ();
progress.Hide (true);
}
}
public void addWaysDatabase ()
{
var ways = Database.Main.Table<Way> ().ToArray ();
foreach (var way in ways)
{
List<CLLocationCoordinate2D> points = new List<CLLocationCoordinate2D> ();
foreach (var node in Database.Main.Table<WayNode> ().Where (x => x.WayId == way.id).ToArray ())
{
points.Add (new CLLocationCoordinate2D (node.lat, node.lon));
}
map.AddOverlay (MKPolyline.FromCoordinates (points.ToArray ()));
}
}
public class MyAnnotation : MKAnnotation
{
private CLLocationCoordinate2D _coordinate;
private string _title, _subtitle;
public override CLLocationCoordinate2D Coordinate {
get { return _coordinate; }
set { _coordinate = value; }
}
public override string Title {
get { return _title; }
}
public override string Subtitle {
get { return _subtitle; }
}
public long NodeId { get; set; }
/// <summary>
/// Need this constructor to set the fields, since the public
/// interface of this class is all READ-ONLY
/// <summary>
public MyAnnotation (CLLocationCoordinate2D coord, string t, string s, long nodeID) : base()
{
_coordinate = coord;
_title = t;
_subtitle = s;
NodeId = nodeID;
}
}
public class PersonAnnotation : MKAnnotation
{
public ParkHopperLibrary.Models.PartyMember PartyMember { get; set; }
private CLLocationCoordinate2D _coordinate;
private string _title, _subtitle;
public override CLLocationCoordinate2D Coordinate {
get { return _coordinate; }
set { _coordinate = value; }
}
public override string Title {
get { return _title; }
}
public override string Subtitle {
get { return _subtitle; }
}
public long NodeId { get; set; }
/// <summary>
/// Need this constructor to set the fields, since the public
/// interface of this class is all READ-ONLY
/// <summary>
public PersonAnnotation (ParkHopperLibrary.Models.PartyMember partyMember) : base()
{
_coordinate = new CLLocationCoordinate2D (partyMember.Latitude, partyMember.Longitude);
_title = partyMember.DisplayName;
_subtitle = (DateTime.UtcNow - partyMember.LastUpdate).ToString ();
PartyMember = partyMember;
}
public UIFont Font {
get { return UIFont.FromName ("Helvetica", 12);}
}
public void UpdatesPerson (ParkHopperLibrary.Models.PartyMember partyMember)
{
PartyMember = partyMember;
_coordinate = new CLLocationCoordinate2D (partyMember.Latitude, partyMember.Longitude);
}
UIImage image;
public UIImage Image (SizeF textSize)
{
if(image == null)
using (var cs = CGColorSpace.CreateDeviceRGB ())
{
UIImage template = Images.Person;
var height = template.Size.Height + textSize.Height;
var width = Math.Max( template.Size.Width , textSize.Width);
using (var context = new CGBitmapContext (IntPtr.Zero,(int) width,(int) height, template.CGImage.BitsPerComponent, 0, cs, CGImageAlphaInfo.PremultipliedLast))
{
//context.ScaleCTM (0.5f, -1);
//context.TranslateCTM (0, 0);
context.TranslateCTM (0, 0);
context.DrawImage (new RectangleF ((width - template.Size.Width) / 2,0, template.Size.Width, template.Size.Height), template.CGImage);
context.SetRGBFillColor (1, 1, 1, 1);
context.SelectFont ("Helvetica", 12f, CGTextEncoding.MacRoman);
// Pretty lame way of measuring strings, as documented:
var ns = new NSString (PartyMember.DisplayName);
UIFont ff = UIFont.FromName ("Helvetica", 10);
//context.ScaleCTM (-1, 1);
float textX = (width - textSize.Width) / 2;
float textY = height - textSize.Height;
context.SetTextDrawingMode (CGTextDrawingMode.Fill);
context.ShowTextAtPoint (textX,textY, PartyMember.DisplayName);
// The big string
context.SetRGBFillColor (0, 0, 0, 1);
context.SetTextDrawingMode (CGTextDrawingMode.Fill);
context.StrokePath ();
image = UIImage.FromImage (context.ToImage ());
}
}
return image;
}
}
public class RestaurantAnnotation : MKAnnotation
{
public Restaurant Restaurant { get; set; }
private CLLocationCoordinate2D _coordinate;
private string _title, _subtitle;
public override CLLocationCoordinate2D Coordinate {
get { return _coordinate; }
set { _coordinate = value; }
}
public override string Title {
get { return _title; }
}
public override string Subtitle {
get { return _subtitle; }
}
public long NodeId { get; set; }
/// <summary>
/// Need this constructor to set the fields, since the public
/// interface of this class is all READ-ONLY
/// <summary>
public RestaurantAnnotation (Restaurant restaurant) : base()
{
_coordinate = new CLLocationCoordinate2D (restaurant.lat, restaurant.lon);
_title = restaurant.Name;
_subtitle = restaurant.Cuisine ;
Restaurant = restaurant;
}
}
public class RestroomAnnotation : MKAnnotation
{
public Restroom Restroom { get; set; }
private CLLocationCoordinate2D _coordinate;
private string _title, _subtitle;
public override CLLocationCoordinate2D Coordinate {
get { return _coordinate; }
set { _coordinate = value; }
}
public override string Title {
get { return _title; }
}
public override string Subtitle {
get { return _subtitle; }
}
public long NodeId { get; set; }
/// <summary>
/// Need this constructor to set the fields, since the public
/// interface of this class is all READ-ONLY
/// <summary>
public RestroomAnnotation (Restroom restroom) : base()
{
_coordinate = new CLLocationCoordinate2D (restroom.lat, restroom.lon);
Restroom = restroom;
}
}
class MapViewDelegate : MKMapViewDelegate
{
MapViewController _viewController;
MKMapRect lastGoodMapRect;
bool manuallyChangingMapRect;
public MapViewDelegate (MapViewController viewController)
{
_viewController = viewController;
}
public override void RegionWillChange (MKMapView mapView, bool animated)
{
if (manuallyChangingMapRect)
return;
lastGoodMapRect = mapView.visibleMapRect;
}
/*
if (_viewController.currentAnnotationView != null)
{
_viewController.currentAnnotationView.Hidden = true;
}
*/
public bool MKMapRectContainsRect (MKMapRect firstRect, MKMapRect secondRect)
{
var rect2 = (new Rectangle ((int)secondRect.MinX, (int)secondRect.MinY, (int)(secondRect.MaxX - secondRect.MinX), (int)(secondRect.MaxY - secondRect.MinY)));
return (new Rectangle ((int)firstRect.MinX, (int)firstRect.MinY, (int)(firstRect.MaxX - firstRect.MinX), (int)(firstRect.MaxY - firstRect.MinY))).Contains (rect2);
}
public override void RegionChanged (MKMapView mapView, bool animated)
{
if (manuallyChangingMapRect || !_viewController.LockMap)
//prevents possible infinite recursion when we call setVisibleMapRect below
return;
bool mapContainsOverlay = MKMapRectContainsRect (mapView.visibleMapRect, _viewController.maxRect);
if (mapContainsOverlay)
{
// The overlay is entirely inside the map view but adjust if user is zoomed out too much...
double widthRatio = _viewController.maxRect.Size.Width / mapView.visibleMapRect.Size.Width;
double heightRatio = _viewController.maxRect.Size.Height / mapView.visibleMapRect.Size.Height;
//adjust ratios as needed
if ((widthRatio < 0.6) || (heightRatio < 0.6))
{
manuallyChangingMapRect = true;
mapView.SetVisibleMapRect (_viewController.maxRect, true);
manuallyChangingMapRect = false;
}
}
else if (!_viewController.maxRect.Intersects (mapView.visibleMapRect))
{
// Overlay is no longer visible in the map view.
// Reset to last "good" map rect...
mapView.SetVisibleMapRect (lastGoodMapRect, true);
}
}
public override MKOverlayView GetViewForOverlay (MKMapView mapView, NSObject overlay)
{
MKOverlayView overlayView = null;
if (overlay is MKPolygon)
{
var polygon = (MKPolygon)overlay;
var polygonView = new MKPolygonView (polygon);
polygonView.FillColor = polygon.Subtitle == "attraction" ? UIColor.Blue : UIColor.Green;
polygonView.StrokeColor = UIColor.Gray;
polygonView.LineWidth = 3;
overlayView = polygonView;
}
else if (overlay is MKPolyline)
{
var polylineView = new MKPolylineView ((MKPolyline)overlay);
polylineView.FillColor = UIColor.Red;
polylineView.StrokeColor = UIColor.Red;
polylineView.LineJoin = MonoTouch.CoreGraphics.CGLineJoin.Round;
polylineView.LineDashPattern = new NSNumber[] { new NSNumber (1), new NSNumber (1) };
polylineView.LineWidth = 3;
overlayView = polylineView;
}
return overlayView;
}
public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, NSObject annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MyAnnotation)
{
var pin = annotation as MyAnnotation;
var pinView = new MKAnnotationView (pin, "colors");
var section = Updates.AttractionSections.Where (x => x.Key == pin.NodeId).Select (y => y.Value).FirstOrDefault () ?? "";
pinView.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
((UIButton)pinView.RightCalloutAccessoryView).TouchDown += delegate {
Console.WriteLine("I was touched!");
};//.AddTarget (this, new Selector ("openSpot:"), UIControlEvent.TouchUpInside);
pinView.Enabled = true;
pinView.CanShowCallout = true;
pinView.CenterOffset = new PointF (8, -13);
pinView.CalloutOffset = new PointF (-8, 0);
pinView.Image = string.IsNullOrEmpty (section) ? Images.GrayPin : Updates.SectionColors[section];
annotationView = pinView;
}
else if (annotation is PersonAnnotation)
{
var person = annotation as PersonAnnotation;
var view = new MKAnnotationView (person, "person");
view.Image = person.Image(view.StringSize(person.PartyMember.DisplayName,person.Font));
//view.SetSelected(true,false);
view.Enabled = true;
view.CanShowCallout = true;
view.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
((UIButton)view.RightCalloutAccessoryView).TouchDown += delegate {
Console.WriteLine("I was touched!");
var alert = new UIAlertView("Are you sure?","Do you want to send " + person.PartyMember.DisplayName + " and update request?",null,"No Thanks","Yes");
alert.Dismissed += delegate(object sender, UIButtonEventArgs e) {
if(e.ButtonIndex > 0){
Settings.ShowNetwork();
}
};
alert.Show();
};
annotationView = view;
}
else if (annotation is RestaurantAnnotation)
{
var resaurant = annotation as RestaurantAnnotation;
var view = new MKAnnotationView (resaurant, "resaurant");
view.Image = Images.Restaurant;
//view.SetSelected(true,false);
view.Enabled = true;
view.CanShowCallout = true;
annotationView = view;
}
else if (annotation is RestroomAnnotation)
{
var restroom = annotation as RestroomAnnotation;
var view = new MKAnnotationView (restroom, "restroom");
view.Image = Images.Restroom;
//view.SetSelected(true,false);
view.Enabled = true;
//view.CanShowCallout = true;
annotationView = view;
}
else if (annotation is GeometryAnnotation || annotation is MergedGeometryAnnotation)
{
Console.WriteLine ("it's a line class");
//GeometryAnnotation geometryAnnotation = annotation as GeometryAnnotation;
LinePolygonAnnotationView _annotationView = new LinePolygonAnnotationView (new RectangleF (0, 0, mapView.Frame.Size.Width, mapView.Frame.Size.Height));
_annotationView.Annotation = annotation;
_annotationView.MapView = mapView;
//_viewController.currentAnnotationView = _annotationView;
annotationView = _annotationView;
}
return annotationView;
}
public override void MapLoaded (MKMapView mapView)
{
}
}
/*
foreach(var person in _viewController.People)
mapView.SelectAnnotation(person,false);
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment