private void Construct(NpgsqlDataReader reader)
        {
            _id        = Convert.ToInt32(reader[Table + "_" + Columns.Id]);
            _name      = Convert.ToString(reader[Table + "_" + Columns.Name]);
            _shapefile = new Shapefile(Convert.ToInt32(reader[Table + "_" + Columns.ShapefileId]));

            NpgsqlCommand cmd = DB.Connection.NewCommand("SELECT " +
                                                         "st_xmin(" + ShapefileGeometry.Columns.Geometry + ") as left," +
                                                         "st_xmax(" + ShapefileGeometry.Columns.Geometry + ") as right," +
                                                         "st_ymin(" + ShapefileGeometry.Columns.Geometry + ") as bottom," +
                                                         "st_ymax(" + ShapefileGeometry.Columns.Geometry + ") as top " +
                                                         "FROM " + _shapefile.GeometryTable);

            reader = cmd.ExecuteReader();

            double left   = double.MaxValue;
            double right  = double.MinValue;
            double bottom = double.MaxValue;
            double top    = double.MinValue;

            while (reader.Read())
            {
                double l = Convert.ToDouble(reader["left"]);
                if (l < left)
                {
                    left = l;
                }

                double r = Convert.ToDouble(reader["right"]);
                if (r > right)
                {
                    right = r;
                }

                double b = Convert.ToDouble(reader["bottom"]);
                if (b < bottom)
                {
                    bottom = b;
                }

                double t = Convert.ToDouble(reader["top"]);
                if (t > top)
                {
                    top = t;
                }
            }

            reader.Close();
            DB.Connection.Return(cmd.Connection);

            _boundingBox = new PostGIS.Polygon(new PostGIS.LineString(new List <PostGIS.Point>(new PostGIS.Point[] {
                new PostGIS.Point(left, top, _shapefile.SRID),
                new PostGIS.Point(right, top, _shapefile.SRID),
                new PostGIS.Point(right, bottom, _shapefile.SRID),
                new PostGIS.Point(left, bottom, _shapefile.SRID),
                new PostGIS.Point(left, top, _shapefile.SRID)
            }), _shapefile.SRID), _shapefile.SRID);
        }
        private void exportThreatSurfaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (CurrentThreatSurface == null)
                MessageBox.Show("No threat surface displayed. Nothing to export.");
            else
            {
                List<string> filters = new List<string>();
                Dictionary<string, ImageFormat> extensionImageFormat = new Dictionary<string, ImageFormat>();
                foreach (ImageFormat format in new ImageFormat[] { ImageFormat.Bmp, ImageFormat.Emf, ImageFormat.Exif, ImageFormat.Gif, ImageFormat.Icon, ImageFormat.Jpeg, ImageFormat.Png, ImageFormat.Tiff, ImageFormat.Wmf })
                {
                    string extension = format.ToString().ToLower();
                    filters.Add(format + " image files (*." + extension + ")|*." + extension);
                    extensionImageFormat.Add(extension, format);
                }

                filters.Add("ESRI shapefiles (*.shp)|*.shp");

                filters.Sort();

                string path = LAIR.IO.File.PromptForSavePath("Select export location...", filters.Concatenate("|"));
                if (path != null)
                {
                    if (File.Exists(path))
                        File.Delete(path);

                    string extension = Path.GetExtension(path).Trim('.').ToLower();

                    if (extensionImageFormat.ContainsKey(extension))
                        CurrentThreatSurface.Save(path, extensionImageFormat[extension]);
                    else if (extension == "shp")
                    {
                        NpgsqlCommand cmd = DB.Connection.NewCommand(null);
                        try
                        {
                            cmd.CommandText = "CREATE TABLE temp (" +
                                              "id SERIAL PRIMARY KEY," +
                                              "region GEOMETRY(POLYGON," + DisplayedPrediction.PredictionArea.Shapefile.SRID + ")," +
                                              "level DOUBLE PRECISION," +
                                              "type VARCHAR)";
                            cmd.ExecuteNonQuery();

                            Dictionary<long, List<Tuple<RectangleF, double, string>>> sliceSquareThreatType = new Dictionary<long, List<Tuple<RectangleF, double, string>>>();
                            GetThreatSurfaces(new Rectangle(0, 0, CurrentThreatSurface.Width, CurrentThreatSurface.Height), false, sliceSquareThreatType);
                            string basePath = path;
                            foreach (long slice in sliceSquareThreatType.Keys)
                            {
                                if (slice != -1)
                                    path = Path.Combine(Path.GetDirectoryName(basePath), Path.GetFileNameWithoutExtension(basePath), "_" + slice, Path.GetExtension(basePath));

                                cmd.CommandText = "DELETE FROM temp";
                                cmd.ExecuteNonQuery();

                                int insertNum = 0;
                                int insertsPerBatch = 500;
                                StringBuilder cmdTxt = new StringBuilder();
                                foreach (Tuple<RectangleF, double, string> squareThreatType in sliceSquareThreatType[slice])
                                {
                                    float bottom = squareThreatType.Item1.Bottom;
                                    float left = squareThreatType.Item1.Left;
                                    float top = squareThreatType.Item1.Top;
                                    float right = squareThreatType.Item1.Right;
                                    PostGIS.Polygon polygon = new PostGIS.Polygon(new PostGIS.Point[]{
                                    GetPostGisPointFromDrawingPoint(new PointF(left, bottom)),
                                    GetPostGisPointFromDrawingPoint(new PointF(left, top)),
                                    GetPostGisPointFromDrawingPoint(new PointF(right, top)),
                                    GetPostGisPointFromDrawingPoint(new PointF(right, bottom)),
                                    GetPostGisPointFromDrawingPoint(new PointF(left, bottom))}, DisplayedPrediction.PredictionArea.Shapefile.SRID);

                                    cmdTxt.Append((cmdTxt.Length == 0 ? "INSERT INTO temp (region,level,type) VALUES " : ",") + "(" + polygon.StGeometryFromText + "," +
                                                                                                                                      squareThreatType.Item2 + "," +
                                                                                                                                      "'" + squareThreatType.Item3 + "')");
                                    if ((insertNum++ % insertsPerBatch) == 0)
                                    {
                                        cmd.CommandText = cmdTxt.ToString();
                                        cmd.ExecuteNonQuery();
                                        cmdTxt.Clear();
                                    }
                                }

                                if (cmdTxt.Length > 0)
                                {
                                    cmd.CommandText = cmdTxt.ToString();
                                    cmd.ExecuteNonQuery();
                                    cmdTxt.Clear();
                                }

                                using (Process process = new Process())
                                {
                                    process.StartInfo.FileName = ATT.Configuration.Pgsql2ShpPath;
                                    process.StartInfo.Arguments = "-f \"" + path + "\" " +
                                                                  "-h " + ATT.Configuration.PostgresHost + " " +
                                                                  "-u " + ATT.Configuration.PostgresUser + " " +
                                                                  "-P " + ATT.Configuration.PostgresPassword + " " +
                                                                  ATT.Configuration.PostgresDatabase + " " +
                                                                  "temp";

                                    process.StartInfo.CreateNoWindow = true;
                                    process.StartInfo.UseShellExecute = false;
                                    process.StartInfo.RedirectStandardError = true;
                                    process.StartInfo.RedirectStandardOutput = true;
                                    process.Start();

                                    Console.Out.WriteLine("Exporting threat surface to shapefile \"" + path + "\".");

                                    string output = process.StandardOutput.ReadToEnd().Trim();
                                    string error = process.StandardError.ReadToEnd().Trim();
                                    Console.Out.WriteLine(output + Environment.NewLine + error);

                                    process.WaitForExit();
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.Out.WriteLine("Error while exporting threat surface as shapefile:  " + ex.Message);
                            throw ex;
                        }
                        finally
                        {
                            try
                            {
                                cmd.CommandText = "DROP TABLE temp";
                                cmd.ExecuteNonQuery();
                            }
                            catch (Exception ex) { Console.Out.WriteLine("Failed to drop temp table for threat surface export:  " + ex.Message); }

                            DB.Connection.Return(cmd.Connection);
                        }
                    }
                    else
                        MessageBox.Show("The file extension \"" + extension + "\" is not a supported export type.");

                    Console.Out.WriteLine("Export finished.");
                }
            }
        }
        private void examinePredictionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DisplayedPrediction == null)
                MessageBox.Show("No prediction displayed. Cannot examine regions.");
            else if (!File.Exists(DisplayedPrediction.PointPredictionLogPath))
                MessageBox.Show("No point prediction information is available.");
            else if (_highlightedThreatRectangle == Rectangle.Empty || _highlightedThreatRectangleCol == -1 || _highlightedThreatRectangleRow == -1)
                MessageBox.Show("Must select a region to examine.");
            else
            {
                float pixelsPerMeter;
                float threatRectanglePixelWidth;
                GetDrawingParameters(new Rectangle(new System.Drawing.Point(0, 0), CurrentThreatSurface.Size), out pixelsPerMeter, out threatRectanglePixelWidth);

                int rowAbsoluteThreatRectangle = _highlightedThreatRectangleRow + (int)(_panOffset.Height / threatRectanglePixelWidth);
                int colAbsoluteThreatRectangle = _highlightedThreatRectangleCol + (int)(_panOffset.Width / threatRectanglePixelWidth);

                if (rowAbsoluteThreatRectangle < 0 || colAbsoluteThreatRectangle < 0)
                    MessageBox.Show("No predictions were made in that region.");
                else
                {
                    float widthMeters = threatRectanglePixelWidth / pixelsPerMeter;
                    float bottomMeters = _regionBottomLeftInMeters.Y + _regionSizeInMeters.Height - (rowAbsoluteThreatRectangle + 1) * widthMeters;
                    float leftMeters = _regionBottomLeftInMeters.X + colAbsoluteThreatRectangle * widthMeters;

                    PostGIS.Polygon threatRectangle = new PostGIS.Polygon(new PostGIS.Point[]{
                                                                          new PostGIS.Point(leftMeters, bottomMeters, DisplayedPrediction.PredictionArea.Shapefile.SRID),
                                                                          new PostGIS.Point(leftMeters, bottomMeters + widthMeters, DisplayedPrediction.PredictionArea.Shapefile.SRID),
                                                                          new PostGIS.Point(leftMeters + widthMeters, bottomMeters + widthMeters, DisplayedPrediction.PredictionArea.Shapefile.SRID),
                                                                          new PostGIS.Point(leftMeters + widthMeters, bottomMeters, DisplayedPrediction.PredictionArea.Shapefile.SRID),
                                                                          new PostGIS.Point(leftMeters, bottomMeters, DisplayedPrediction.PredictionArea.Shapefile.SRID)}, DisplayedPrediction.PredictionArea.Shapefile.SRID);

                    DiscreteChoiceModel model = DisplayedPrediction.Model;

                    PointPrediction[] pointPredictions = PointPrediction.GetWithin(threatRectangle, DisplayedPrediction).ToArray();

                    // only get point predictions in current slice if we've got a timeslice model
                    if (model is TimeSliceDCM)
                        pointPredictions = pointPredictions.Where(p => (p.Time.Ticks / (model as TimeSliceDCM).TimeSliceTicks) == timeSlice.Value).ToArray();

                    if (pointPredictions.Length > 0)
                    {
                        DataGridView dataView = new DataGridView();
                        dataView.ReadOnly = true;
                        dataView.AllowUserToAddRows = false;

                        int predictionIdCol = dataView.Columns.Add("prediction_id", "Prediction ID");

                        Dictionary<string, int> incidentProbCol = new Dictionary<string, int>();
                        Set<int> incidentCols = new Set<int>();
                        foreach (string incident in pointPredictions[0].IncidentScore.Keys)
                        {
                            string colName = "Threat:  " + incident;
                            int incidentCol = dataView.Columns.Add(colName, colName);
                            incidentProbCol.Add(incident, incidentCol);
                            incidentCols.Add(incidentCol);
                        }

                        Dictionary<string, int> featureIdCol = new Dictionary<string, int>();
                        Set<int> featureCols = new Set<int>();
                        if (model is IFeatureBasedDCM)
                        {
                            IFeatureBasedDCM featureBasedModel = DisplayedPrediction.Model as IFeatureBasedDCM;
                            foreach (Feature feature in featureBasedModel.Features.OrderBy(f => f.Id))
                            {
                                string colName = "Feature:  " + feature.ToString();
                                int featureCol = dataView.Columns.Add(colName, colName);
                                featureIdCol.Add(feature.Id, featureCol);
                                featureCols.Add(featureCol);
                            }
                        }

                        dataView.Rows.Add(pointPredictions.Length);

                        try
                        {
                            Set<string> logPointIdsToGet = new Set<string>(pointPredictions.Select(p => model.GetPointIdForLog(p.PointId, p.Time)).ToArray());
                            Dictionary<string, Tuple<List<Tuple<string, double>>, List<Tuple<string, string>>>> pointPredictionLog = model.ReadPointPredictionLog(DisplayedPrediction.PointPredictionLogPath, logPointIdsToGet);
                            for (int i = 0; i < pointPredictions.Length; ++i)
                            {
                                PointPrediction pointPrediction = pointPredictions[i];

                                dataView[predictionIdCol, i].Value = pointPrediction.PointId;

                                string logPointId = model.GetPointIdForLog(pointPrediction.PointId, pointPrediction.Time);

                                foreach (Tuple<string, double> labelConfidence in pointPredictionLog[logPointId].Item1)
                                    if (labelConfidence.Item1 != PointPrediction.NullLabel)
                                        dataView[incidentProbCol[labelConfidence.Item1], i].Value = labelConfidence.Item2;

                                foreach (Tuple<string, string> featureIdValue in pointPredictionLog[logPointId].Item2)
                                    dataView[featureIdCol[featureIdValue.Item1], i].Value = featureIdValue.Item2;
                            }
                        }
                        catch (Exception ex) { Console.Out.WriteLine("Error while reading prediction log:  " + ex.Message); }

                        dataView.SortCompare += new DataGridViewSortCompareEventHandler(delegate(object o, DataGridViewSortCompareEventArgs args)
                            {
                                int sortedColumn = args.Column.DisplayIndex;

                                if (args.CellValue1 == null && args.CellValue2 == null)
                                    args.SortResult = args.RowIndex1.CompareTo(args.RowIndex2);
                                else if (args.CellValue1 == null || args.CellValue2 == null)
                                    args.SortResult = args.CellValue1 == null ? -1 : 1;
                                else if (sortedColumn == predictionIdCol)
                                    args.SortResult = ((int)args.CellValue1).CompareTo((int)args.CellValue2);
                                else if (incidentCols.Contains(sortedColumn))
                                    args.SortResult = ((double)args.CellValue1).CompareTo((double)args.CellValue2);
                                else if (featureCols.Contains(sortedColumn))
                                {
                                    double cellValue1, cellValue2;
                                    if (double.TryParse(args.CellValue1.ToString(), out cellValue1) && double.TryParse(args.CellValue2.ToString(), out cellValue2))
                                        args.SortResult = Math.Abs(cellValue1).CompareTo(Math.Abs(cellValue2));
                                    else
                                        args.SortResult = args.CellValue1.ToString().CompareTo(args.CellValue2.ToString());
                                }
                                else
                                    throw new Exception("Unknown column type");

                                args.Handled = true;
                            });

                        PredictionDataGridViewForm form = new PredictionDataGridViewForm();
                        form.MaximumSize = Screen.FromControl(this).Bounds.Size;
                        form.Controls.Add(dataView);
                        form.Show();
                        dataView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
                        form.ClientSize = dataView.Size = dataView.PreferredSize;
                        form.Location = new System.Drawing.Point(0, 0);
                    }
                    else
                        MessageBox.Show("No predictions were made in that region.");
                }
            }
        }
        private void Construct(NpgsqlDataReader reader)
        {
            _id = Convert.ToInt32(reader[Table + "_" + Columns.Id]);
            _name = Convert.ToString(reader[Table + "_" + Columns.Name]);
            _shapefile = new Shapefile(Convert.ToInt32(reader[Table + "_" + Columns.ShapefileId]));

            NpgsqlCommand cmd = DB.Connection.NewCommand("SELECT " +
                                                         "st_xmin(" + ShapefileGeometry.Columns.Geometry + ") as left," +
                                                         "st_xmax(" + ShapefileGeometry.Columns.Geometry + ") as right," +
                                                         "st_ymin(" + ShapefileGeometry.Columns.Geometry + ") as bottom," +
                                                         "st_ymax(" + ShapefileGeometry.Columns.Geometry + ") as top " +
                                                         "FROM " + _shapefile.GeometryTable);
            reader = cmd.ExecuteReader();

            double left = double.MaxValue;
            double right = double.MinValue;
            double bottom = double.MaxValue;
            double top = double.MinValue;
            while (reader.Read())
            {
                double l = Convert.ToDouble(reader["left"]);
                if (l < left)
                    left = l;

                double r = Convert.ToDouble(reader["right"]);
                if (r > right)
                    right = r;

                double b = Convert.ToDouble(reader["bottom"]);
                if (b < bottom)
                    bottom = b;

                double t = Convert.ToDouble(reader["top"]);
                if (t > top)
                    top = t;
            }

            reader.Close();
            DB.Connection.Return(cmd.Connection);

            _boundingBox = new PostGIS.Polygon(new PostGIS.LineString(new List<PostGIS.Point>(new PostGIS.Point[]{
                               new PostGIS.Point(left, top, _shapefile.SRID),
                               new PostGIS.Point(right, top, _shapefile.SRID),
                               new PostGIS.Point(right, bottom, _shapefile.SRID),
                               new PostGIS.Point(left, bottom, _shapefile.SRID),
                               new PostGIS.Point(left, top, _shapefile.SRID)}), _shapefile.SRID), _shapefile.SRID);
        }