TrackCars.cs

This example demonstrates how to simulate motion of vehicles based on GPS data. Here is a screenshot with the results of the code execution.

TrackCars.png
using System.Collections.Generic;
using System.Diagnostics;
using AxMapWinGIS;
using MapWinGIS;
using System;
using System.IO;
using System.Windows.Forms;
namespace Examples
{
public partial class MapExamples
{
private Shapefile _carShapefile;
public void TrackCars(AxMap axMap1, string dataPath)
{
if (!InitMap(axMap1, dataPath))
return;
string filename = dataPath + "path.shp";
if (!File.Exists(filename))
{
MessageBox.Show("Path.shp wasn't found: " + dataPath);
return;
}
int handle = axMap1.AddLayerFromFilename(filename, tkFileOpenStrategy.fosAutoDetect, false);
var sf = axMap1.get_Shapefile(handle);
var service = new CarService(axMap1.Extents as Extents, sf, 20);
_carShapefile = CreateCarShapefile(service);
_carShapefile.Volatile = true;
_carShapefile.CollisionMode = tkCollisionMode.AllowCollisions;
axMap1.AddLayer(_carShapefile, true);
axMap1.ZoomToLayer(handle);
service.StateChanged += ServiceStateChanged;
}
private bool InitMap(AxMap axMap1, string dataPath)
{
axMap1.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR;
axMap1.GrabProjectionFromData = true;
axMap1.ZoomBehavior = tkZoomBehavior.zbUseTileLevels;
axMap1.DisableWaitCursor = true;
string filename1 = dataPath + "buildings.shp";
string filename2 = dataPath + "roads.shp";
if (!File.Exists(filename1) || !File.Exists(filename2))
{
MessageBox.Show("Couldn't find the files (buildings.shp, roads.shp): " + dataPath);
return false;
}
Shapefile sf = new Shapefile();
sf.Open(filename1, null);
axMap1.AddLayer(sf, true);
Debug.Print(axMap1.GeoProjection.ExportToWKT());
sf = new Shapefile();
sf.Open(filename2, null);
sf.Labels.Generate("[Name]", tkLabelPositioning.lpLongestSegement, false);
axMap1.ZoomToMaxExtents();
return true;
}
private Shapefile CreateCarShapefile(CarService service)
{
var sf = new Shapefile();
sf.CreateNew("", ShpfileType.SHP_POINT);
sf.DefaultDrawingOptions.AlignPictureByBottom = false;
var ct = sf.Categories.Add("Police");
var opt = ct.DrawingOptions;
opt.PointType = tkPointSymbolType.ptSymbolPicture;
opt.Picture = IconManager.GetIcon(CarType.Police);
ct = sf.Categories.Add("Taxi");
opt = ct.DrawingOptions;
opt.PointType = tkPointSymbolType.ptSymbolPicture;
opt.Picture = IconManager.GetIcon(CarType.Taxi);
ct = sf.Categories.Add("Ambulance");
opt = ct.DrawingOptions;
opt.PointType = tkPointSymbolType.ptSymbolPicture;
opt.Picture = IconManager.GetIcon(CarType.Ambulance);
// general settings for labels should be applied before creating categories
sf.Labels.FrameVisible = true;
sf.Labels.TextRenderingHint = tkTextRenderingHint.ClearTypeGridFit;
var utils = new Utils();
var lb = sf.Labels.AddCategory("Busy");
lb.FrameBackColor = utils.ColorByName(tkMapColor.Yellow);
lb = sf.Labels.AddCategory("Available");
lb.FrameBackColor = utils.ColorByName(tkMapColor.Green);
lb = sf.Labels.AddCategory("OutOfService");
lb.FrameBackColor = utils.ColorByName(tkMapColor.Gray);
Debug.Print("Num categories: " + sf.Labels.NumCategories);
for (int i = 0; i < service.Cars.Count; i++)
{
var car = service.Cars[i];
var shp = new Shape();
shp.Create(ShpfileType.SHP_POINT);
shp.AddPoint(car.X, car.Y);
sf.EditAddShape(shp);
sf.ShapeCategory[i] = (int) car.CarType;
sf.Labels.AddLabel(car.ToString(), car.X, car.Y);
sf.Labels.Label[i, 0].Category = (int)(car.State);
}
return sf;
}
private void ServiceStateChanged(object sender, EventArgs e)
{
var service = sender as CarService;
if (service == null) return;
for (int i = 0; i < service.Cars.Count; i++)
{
var car = service.Cars[i];
if (i < _carShapefile.NumShapes)
{
_carShapefile.Shape[i].put_XY(0, car.X, car.Y);
_carShapefile.ShapeRotation[i] = car.Direction;
var lbl = _carShapefile.Labels.Label[i, 0];
lbl.x = car.X + 20;
lbl.y = car.Y;
}
}
axMap1.Redraw2(tkRedrawType.RedrawSkipDataLayers);
}
}
public enum CarState
{
Busy = 0,
Avaiable = 1,
OutOfService = 2
}
public enum CarType
{
Police = 0,
Taxi = 1,
Ambulance = 2
}
public class GeometryHelper
{
public static double GetDirection(double x, double y)
{
if (Math.Abs(y) > 1e-10)
{
double angle = Math.Atan(x / y);
if (y < 0)
{
return Math.PI + angle;
}
if (x >= 0)
{
return angle;
}
return 2.0 * Math.PI + angle;
}
if (x > 0)
{
return Math.PI / 2.0;
}
if (x < 0)
{
return 1.5 * Math.PI;
}
return 0.0;
}
}
public class Car
{
private double _distance;
public Car(Shape path, Random rnd)
{
Path = path;
_distance = path.Length * rnd.NextDouble();
}
#region Properties
public int Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Direction { get; set; }
public double Speed { get; set; } // km per hour
public CarState State { get; set; }
public CarType CarType { get; set; }
public Shape Path { get; set; }
public double MetersPerSecond
{
get { return Speed * 1000 / 3600; }
}
#endregion
#region Methods
public override string ToString()
{
return CarType + " " + Id;
}
public void Move(int interval)
{
double step = MetersPerSecond * interval / 1000; // meters
_distance += step;
if (_distance > Path.Length) _distance = 0;
//calculating the current position (x2, y2)
double dist = 0.0;
double x1, x2, y1, y2;
x1 = x2 = y1 = y2 = 0.0;
for (int i = 1; i < Path.numPoints; i++)
{
Path.get_XY(i, ref x2, ref y2);
Path.get_XY(i - 1, ref x1, ref y1);
double val = Math.Sqrt(Math.Pow(x2 - x1, 2.0) + Math.Pow(y2 - y1, 2.0));
if (dist + val > _distance)
{
double ratio = (_distance - dist) / val;
x2 = x1 + (x2 - x1) * ratio;
y2 = y1 + (y2 - y1) * ratio;
break;
}
if (dist + val < _distance)
{
dist += val;
}
else
{
break;
}
}
Direction = GeometryHelper.GetDirection(x2 - X, y2 - Y) / Math.PI * 180.0;
X = x2;
Y = y2;
}
#endregion
}
public class IconManager
{
private static Dictionary<CarType, Image> _dict = new Dictionary<CarType, Image>();
private static string _iconPath = Path.GetDirectoryName(Application.ExecutablePath) + @"..\..\..\..\icons\";
static IconManager()
{
foreach (CarType val in Enum.GetValues(typeof(CarType)))
{
string path = IconPathForCarType(val);
if (File.Exists(path))
{
var img = new Image();
if (img.Open(path))
{
_dict.Add(val, img);
}
}
}
}
private static string IconPathForCarType(CarType type)
{
string path = _iconPath;
switch (type)
{
case CarType.Police:
return path + "police_car.png";
case CarType.Taxi:
return path + "taxi_car.png";
case CarType.Ambulance:
return path + "ambulance_car.png";
}
return string.Empty;
}
public static Image GetIcon(CarType type)
{
return _dict.ContainsKey(type) ? _dict[type] : null;
}
}
public class CarService
{
private const int UPDATE_INTERVAL = 200; // milliseconds
public List<Car> Cars = new List<Car>();
public event EventHandler StateChanged;
private Timer _timer = new Timer();
private Random _random = new Random();
private void FireStateChanged()
{
var handler = StateChanged;
if (handler != null)
handler(this, new EventArgs());
}
public CarService(Extents ext, Shapefile paths, int numCars)
{
if (ext == null)
throw new NullReferenceException();
CreateCars(ext, paths, numCars);
UpdatePositions();
InitTimer();
}
private void CreateCars(Extents ext, Shapefile paths, int numCars)
{
Cars.Clear();
for (int i = 0; i < numCars; i++)
{
var path = paths.Shape[0]; //paths.Shape[_random.Next(paths.NumShapes - 1)];
double x = ext.xMin + (ext.xMax - ext.xMin) * _random.NextDouble();
double y = ext.yMin + (ext.yMax - ext.yMin) * _random.NextDouble();
var car = new Car(path, _random)
{
Id = i,
X = x,
Y = y,
Speed = 30 + 50 * _random.NextDouble(),
CarType = (CarType)_random.Next(3),
State = (CarState)_random.Next(3),
};
Cars.Add(car);
}
}
private void InitTimer()
{
_timer.Interval = UPDATE_INTERVAL;
_timer.Start();
_timer.Tick += (s, e) => UpdatePositions();
}
private void UpdatePositions()
{
foreach (var car in Cars)
{
car.Move(UPDATE_INTERVAL);
}
FireStateChanged();
}
}
}