/// <summary> /// This is a console example of importing Zones from a .csv file. /// /// 1) Process command line arguments: Server, Database, Username, Password, Options and Load .csv file. /// Note: the .csv file in this project is a sample, you may need to change entries (such as group names) for the example to work. /// 2) Create Geotab API object and Authenticate. /// 3) Import zones into database. /// /// A complete Geotab API object and method reference is available at the Geotab Developer page. /// </summary> /// <param name="args">The command line arguments for the application. Note: When debugging these can be added by: Right click the project > Properties > Debug Tab > Start Options: Command line arguments.</param> static void Main(string[] args) { try { if (args.Length < 5) { Console.WriteLine(); Console.WriteLine("Command line parameters:"); Console.WriteLine("dotnet run <server> <database> <username> <password> [-poly=<##>] [-type=<name>] <inputfile>"); Console.WriteLine(); Console.WriteLine("Command line: dotnet run server database username password -poly=6 -type=home inputfile"); Console.WriteLine("server - The server name (Example: my.geotab.com)"); Console.WriteLine("database - The database name (Example: G560)"); Console.WriteLine("username - The Geotab user name"); Console.WriteLine("password - The Geotab password"); Console.WriteLine("--poly=## - Optional - Draw the zone as an N-sided polygon. (Default: 4)"); Console.WriteLine("--type=<name> - Optional - Specify zone type as customer, home, or office. (Default: customer)"); Console.WriteLine("inputfile - File name of the CSV file to import."); Console.WriteLine(); return; } // Variables from command line int last = args.Length - 1; string server = args[0]; string database = args[1]; string username = args[2]; string password = args[3]; string filename = args[last]; int polygonSides = 4; IList <ZoneType> zoneTypes = new List <ZoneType>(); // Options from args for (int i = 4; i < last; i++) { string option = args[i].ToLowerInvariant(); // Poly sides option if (option.Contains("poly")) { int index = option.IndexOf('='); if (index >= 0 && option.Length > index + 1) { if (int.TryParse(option.Substring(index + 1), out int value) && value > 2) { polygonSides = value; } } } // Zone type option else if (option.Contains("type")) { int index = option.IndexOf('='); if (index >= 0 && option.Length > index + 1) { string value = option.Substring(index + 1).ToLowerInvariant(); ZoneType zoneType = null; if (value.Contains("customer")) { zoneType = ZoneTypeCustomer.Value; } else if (value.Contains("home")) { zoneType = ZoneTypeHome.Value; } else if (value.Contains("office")) { zoneType = ZoneTypeOffice.Value; } if (zoneType != null && !zoneTypes.Contains(zoneType)) { zoneTypes.Add(zoneType); } } } } // Use Customer Zone Type for default. if (zoneTypes.Count == 0) { zoneTypes.Add(ZoneTypeCustomer.Value); } // Load .csv file entries into a collection of customers Console.WriteLine("Loading CSV file..."); List <ZoneRow> zoneRows; try { zoneRows = LoadZonesFromCSV(filename); } catch (Exception exception) { Console.WriteLine($"Could not load CSV file: {exception.Message}"); return; } // Create Geotab API object API api = new API(username, password, null, database, server); // Authenticate Console.WriteLine("Authenticating..."); api.Authenticate(); // Get user User user = api.Call <User>("RefreshUser"); // Start import Console.WriteLine("Importing..."); IList <Zone> zones = api.Call <IList <Zone> >("Get", typeof(Zone)); IList <Group> allGroups = api.Call <IList <Group> >("Get", typeof(Group)); // We only want to be able to assign Organization Group if the API user has this in their scope. bool hasOrgGroupScope = false; // See if the API user has Organization Group in their Groups foreach (Group group in user.CompanyGroups) { if (group is CompanyGroup || group is RootGroup) { hasOrgGroupScope = true; break; } } // Set the zone color based on type. // Geotab uses standard colors for the stock zone types. Colors are noted at the top of this class. Color zoneColor = customerZoneColor; if (zoneTypes.Count > 0) { ZoneType type = zoneTypes[0]; if (type is ZoneTypeHome) { zoneColor = homeZoneColor; } else if (type is ZoneTypeOffice) { zoneColor = officeZoneColor; } } // Add zones foreach (ZoneRow zoneRow in zoneRows) { Group group; // If there are no nodes for the zone specified in the .csv we will try to assign to Organization if (hasOrgGroupScope && string.IsNullOrEmpty(zoneRow.NodeName)) { group = new CompanyGroup(); } // Organization group else if (hasOrgGroupScope && zoneRow.NodeName.Trim().ToLowerInvariant() == "organization" || zoneRow.NodeName.Trim().ToLowerInvariant() == "entire organization") { group = new CompanyGroup(); } else { // Get the group from allGroups group = GetGroup(zoneRow.NodeName.Trim(), allGroups); if (group == null) { Console.WriteLine($"Zone Rejected: '{zoneRow.Name}'. Group: '{zoneRow.NodeName}' does not exist."); continue; } } // Check for an existing zone if (ZoneExists(zoneRow.Name, zoneRow.NodeName, zones)) { Console.WriteLine($"Zone exists: '{zoneRow.Name}'."); continue; } // Check for an existing zone try { // Create a new zone object Zone zone = CreateCircleZone(zoneRow.Name, "", zoneRow.Latitude, zoneRow.Longitude, zoneRow.Size, polygonSides, zoneTypes, zoneColor, new List <Group> { group }); api.Call <Id>("Add", typeof(Zone), new { entity = zone }); Console.WriteLine($"Zone: '{zoneRow.Name}' added"); } catch (Exception ex) { // Catch and display any error that occur when adding the zone Console.WriteLine($"Error adding zone: '{zoneRow.Name}'\n{ex.Message}"); } } } catch (Exception ex) { // Show miscellaneous exceptions Console.WriteLine($"Error: {ex.Message}\n{ex.StackTrace}"); } finally { Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); } }
/// <summary> /// Create a new circle zone. /// </summary> /// <param name="name">The zone name.</param> /// <param name="comment">The zone comment.</param> /// <param name="longitude">The longitude of the center point of the zone.</param> /// <param name="latitude">The latitude of the center point of the zone.</param> /// <param name="size">The size.</param> /// <param name="polygonSides">The number of polygon sides.</param> /// <param name="zoneTypes">The zone types.</param> /// <param name="color">The color.</param> /// <param name="groups">The nodes.</param> /// <returns> /// A new zone. /// </returns> static Zone CreateCircleZone(string name, string comment, double longitude, double latitude, double size, int polygonSides, IList <ZoneType> zoneTypes, Color color, IList <Group> groups) { double diagonal = 0.001F * size; IList <ISimpleCoordinate> circleCoordinates = GetCircleCoordinates(new Coordinate(longitude, latitude), diagonal / 2, polygonSides); return(new Zone(null, name, comment, true, zoneTypes, circleCoordinates, DateTime.MinValue, System.TimeZoneInfo.ConvertTimeToUtc(DateTime.MaxValue), color, true, groups)); }
/// <summary> /// This is a console example of importing zones of a specified type from a shape file set (.shp, .shx, .dbf) or csv file into a specified database. /// Included in this project is are sample shape files: owensboro /// /// 1) Process command line arguments: Server, Database, User name, Password, Options and load the specified shape/csv file set. /// 2) Process the shapes present in the shape file, using the Geotab.Geographical components to create zones from the shape points and names. /// 3) Create Geotab API object and Authenticate. /// 4) Import created zones into the database. /// The format of csv file is next: /// [Zone_name] /// [polygon_point_x], [polygon_point_y] /// [polygon_point_x], [polygon_point_y] /// . /// . /// [polygon_point_x], [polygon_point_y] /// [Zone_name] /// [polygon_point_x], [polygon_point_y] /// . /// . /// A complete Geotab API object and method reference is available at the Geotab Developer page. /// </summary> /// <param name="args">The command line arguments for the application. Note: When debugging these can be added by: Right click the project > Properties > Debug Tab > Start Options: Command line arguments.</param> static void Main(string[] args) { try { if (args.Length < 5) { Console.WriteLine(); Console.WriteLine("Command line parameters:"); Console.WriteLine("dotnet run <server> <database> <login> <password> [--nameAttr=<attr>] [--namePrefix=<prefix>] [--type=<name>] <inputfile>"); Console.WriteLine(); Console.WriteLine("Command line: dotnet run server database username password --nameAttr=name --namePrefix=CA --type=home inputfile.shp"); Console.WriteLine("server - The server name (Example: my.geotab.com)"); Console.WriteLine("database - The database name (Example: G560)"); Console.WriteLine("username - The Geotab user name"); Console.WriteLine("password - The Geotab password"); Console.WriteLine("[--nameAttr=<attr>] - Optional - Name of the shape's attribute to use as"); Console.WriteLine(" the zone name. (Default: first attribute containing"); Console.WriteLine(" the word 'name')"); Console.WriteLine("[--namePrefix=<prefix>] - Optional - The name prefix. (Example: prefix + Name)"); Console.WriteLine("[--type=<name>] - Optional - Specify zone type (Default: customer)"); Console.WriteLine("[--threshold=<number>] - Optional - Simplify zones by removing redundant"); Console.WriteLine(" points. Any point within approximately <threshold>"); Console.WriteLine(" meters of a line connecting its neighbors will be"); Console.WriteLine(" removed."); Console.WriteLine("inputfile - File name of the Shape file containing the shapes to"); Console.WriteLine(" import with extension. (.shp, .shx, .dbf)"); Console.WriteLine(); return; } // Variables from command line int last = args.Length - 1; string server = args[0]; string database = args[1]; string username = args[2]; string password = args[3]; List <ZoneType> zoneTypes = new List <ZoneType>(); string fileName = args[last]; string nameAttribute = null; string prefix = ""; double distanceSquaredError = -1; Color zonesColour = customerZoneColor; // Create Geotab API object API api = new API(username, password, null, database, server); // Authenticate Console.WriteLine("Authenticating..."); api.Authenticate(); Console.WriteLine("Getting current user..."); List <User> users = api.Call <List <User> >("Get", typeof(User), new { search = new UserSearch { Name = api.UserName } }); if (users.Count != 1) { Console.WriteLine($"Found {users.Count} users with name {api.UserName}."); return; } // the user's groups will be used when creating the zones var groups = users[0].CompanyGroups; Console.WriteLine("Getting available zone types..."); List <ZoneType> availableZoneTypes = api.Call <List <ZoneType> >("Get", typeof(ZoneType)); // Options from args for (int i = 4; i < last; i++) { string option = args[i].ToLowerInvariant(); int index = option.IndexOf('='); // Check the option is in the established format if (index >= 0 && option.Length > index + 1) { // Grab the value of the optional argument string value = option.Substring(index + 1).ToLowerInvariant(); // Zone type option if (option.Contains("type")) { ZoneType zoneType = GetZoneType(value, availableZoneTypes); if (zoneType != null && !zoneTypes.Contains(zoneType)) { // Setting a default colour switch (zoneType.Name) { case "**Customer Zone": zonesColour = customerZoneColor; break; case "**Home Zone": zonesColour = homeZoneColor; break; case "**Office Zone": zonesColour = officeZoneColor; break; } zoneTypes.Add(zoneType); } } // Name attribute option else if (option.Contains("nameattr")) { nameAttribute = value; } // Name prefix option else if (option.Contains("prefix")) { prefix = value.ToUpperInvariant(); } // Zone shape simplification threshold option else if (option.Contains("threshold")) { if (!double.TryParse(value, out double threshold)) { Console.WriteLine("Threshold must be a number. Example: 2.5"); } else { distanceSquaredError = Math.Pow(1e-5 * threshold, 2); } } else { Console.WriteLine($"Unknown optional argument: {option}"); } } else { Console.WriteLine($"Unknown format for optional argument: {option}"); } } // Use Customer Zone Type for default. if (zoneTypes.Count < 1) { zoneTypes.Add(ZoneTypeCustomer.Value); } IList <ISimpleCoordinate> coordinates = new List <ISimpleCoordinate>(); IList <Zone> zones = new List <Zone>(); // Initialize variables to hold the shape file DateTime maxValue = System.TimeZoneInfo.ConvertTimeToUtc(DateTime.MaxValue); DateTime minValue = DateTime.MinValue; if (!string.IsNullOrEmpty(fileName) && Path.GetExtension(fileName).ToLower().Contains("csv")) { if (!File.Exists(fileName)) { Console.WriteLine($"The file {fileName} does not exist."); return; } Console.WriteLine("Loading csv file..."); using (StreamReader reader = File.OpenText(fileName)) { string line; string zoneName = string.Empty; while (!string.IsNullOrEmpty(line = reader.ReadLine())) { string[] values = line.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (values.Length == 0 || values.Length == 1 && string.IsNullOrWhiteSpace(values[0])) { continue; } switch (values.Length) { case 1: if (!string.IsNullOrEmpty(values[0])) { if (!string.IsNullOrEmpty(zoneName)) { // Simplify the zone shape. This is an important step. Polygon complexity can drastically impact performance when loading/rendering zones. coordinates = SimplifyPolygon(coordinates, distanceSquaredError); // Create the zone object to be inserted later on zones.Add(new Zone(null, (string.IsNullOrEmpty(prefix) ? "" : prefix + " ") + zoneName, "", true, zoneTypes, coordinates, minValue, maxValue, zonesColour, true, groups)); } coordinates = new List <ISimpleCoordinate>(); zoneName = values[0]; } break; case 2: // read coordinates line by line if (double.TryParse(values[0], out var xCoord) && double.TryParse(values[1], out var yCoord)) { coordinates.Add(new Coordinate(xCoord, yCoord)); } break; default: Console.WriteLine($"Skipping a line with text '{line}'"); break; } } if (!string.IsNullOrEmpty(zoneName)) { coordinates = SimplifyPolygon(coordinates, distanceSquaredError); zones.Add(new Zone(null, (string.IsNullOrEmpty(prefix) ? "" : prefix + " ") + zoneName, "", true, zoneTypes, coordinates, minValue, maxValue, zonesColour, true, groups)); } } } else { FileInfo shapeFile = new FileInfo(fileName); FileMapLayerFactory factory = new FileMapLayerFactory(shapeFile); ShapeFileLayer layer; // Try to load the shape file Console.WriteLine("Loading shape file..."); try { layer = (ShapeFileLayer)factory.GetMapLayer(); } catch (Exception exception) { Console.WriteLine($"Could not load shape file: {exception.Message}"); return; } // Get the index of the feature attribute that holds the zone name Console.WriteLine("__ " + nameAttribute); int nameAttributeIndex = GetNameAttributeIndex(layer, ref nameAttribute); if (nameAttributeIndex == -1) { Console.WriteLine("Could not find a valid attribute to use for naming the zones."); return; } // Process shapes into zones int featureIndex = 0; for (int k = 0; k < layer.Features.Count; k++) { IEarthFeature earthFeature = layer.Features[k]; // Get the data we are interested in for the zone IEarthPolygon polygon = earthFeature as IEarthPolygon; string zoneName = layer.GetFeatureField(featureIndex, nameAttributeIndex).ToString(); // Filter out non-polygons and unnamed shapes if (polygon == null || string.IsNullOrEmpty(zoneName)) { featureIndex++; continue; } // Get the points that define the polygon coordinates = new List <ISimpleCoordinate>(); for (int i = 0; i < polygon.Count; i++) { EarthPoint earthPoint = (EarthPoint)polygon[i]; ISimpleCoordinate coordinate = new Coordinate(earthPoint.X, earthPoint.Y); coordinates.Add(coordinate); } // Simplify the polygon. This is an important step. Polygon complexity can drastically impact performance when loading/rendering zones. coordinates = SimplifyPolygon(coordinates, distanceSquaredError); zones.Add(new Zone(null, (string.IsNullOrEmpty(prefix) ? "" : prefix + " ") + zoneName, "", true, zoneTypes, coordinates, minValue, maxValue, zonesColour, true, groups)); featureIndex++; } } Console.WriteLine($"Found {zones.Count} zones to import"); if (zones.Count < 1) { return; } // Start import Console.WriteLine("Importing zones..."); foreach (Zone zone in zones) { try { // Add the zone api.Call <Id>("Add", typeof(Zone), new { entity = zone }); Console.WriteLine($"Zone: '{zone.Name}' added"); } catch (Exception exception) { // Catch and display any error that occur when adding the zone Console.WriteLine($"Error adding zone: '{zone.Name}'\n{exception.Message}"); } } } catch (Exception ex) { // Show miscellaneous exceptions Console.WriteLine($"Error: {ex.Message}\n{ex.StackTrace}"); } finally { Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); } }