private async Task <Record> ProcessFile(object a)
        {
            ThreadInfo threadInfo = a as ThreadInfo;
            Record     r          = threadInfo.Record;
            await Task.Factory.StartNew(async() =>
            {
                var progressValue = threadInfo.ProgressHandler as IProgress <int>;
                string outPath    = threadInfo.OutPath;
                int length        = threadInfo.Length;

                string photo;
                string path;
                Bitmap image                  = new Bitmap(threadInfo.File);
                PropertyItem[] propItems      = image.PropertyItems;
                PropertyItem propItemLatRef   = image.GetPropertyItem(0x0001);
                PropertyItem propItemLat      = image.GetPropertyItem(0x0002);
                PropertyItem propItemLonRef   = image.GetPropertyItem(0x0003);
                PropertyItem propItemLon      = image.GetPropertyItem(0x0004);
                PropertyItem propItemAltRef   = image.GetPropertyItem(0x0005);
                PropertyItem propItemAlt      = image.GetPropertyItem(0x0006);
                PropertyItem propItemSat      = image.GetPropertyItem(0x0008);
                PropertyItem propItemDir      = image.GetPropertyItem(0x0011);
                PropertyItem propItemVel      = image.GetPropertyItem(0x000D);
                PropertyItem propItemPDop     = image.GetPropertyItem(0x000B);
                PropertyItem propItemDateTime = image.GetPropertyItem(0x0132);
                RecordUtil RecordUtil         = new RecordUtil(r);
                propItemLat      = RecordUtil.getEXIFCoordinate("latitude", propItemLat);
                propItemLon      = RecordUtil.getEXIFCoordinate("longitude", propItemLon);
                propItemAlt      = RecordUtil.getEXIFNumber(propItemAlt, "altitude", 10);
                propItemLatRef   = RecordUtil.getEXIFCoordinateRef("latitude", propItemLatRef);
                propItemLonRef   = RecordUtil.getEXIFCoordinateRef("longitude", propItemLonRef);
                propItemLonRef   = RecordUtil.getEXIFCoordinateRef("longitude", propItemLonRef);
                propItemAltRef   = RecordUtil.getEXIFAltitudeRef(propItemAltRef);
                propItemDir      = RecordUtil.getEXIFNumber(propItemDir, "bearing", 10);
                propItemVel      = RecordUtil.getEXIFNumber(propItemVel, "velocity", 100);
                propItemPDop     = RecordUtil.getEXIFNumber(propItemPDop, "pdop", 10);
                propItemSat      = RecordUtil.getEXIFInt(propItemSat, r.Satellites);
                propItemDateTime = RecordUtil.getEXIFDateTime(propItemDateTime);
                RecordUtil       = null;
                image.SetPropertyItem(propItemLat);
                image.SetPropertyItem(propItemLon);
                image.SetPropertyItem(propItemLatRef);
                image.SetPropertyItem(propItemLonRef);
                image.SetPropertyItem(propItemAlt);
                image.SetPropertyItem(propItemAltRef);
                image.SetPropertyItem(propItemDir);
                image.SetPropertyItem(propItemVel);
                image.SetPropertyItem(propItemPDop);
                image.SetPropertyItem(propItemSat);
                image.SetPropertyItem(propItemDateTime);
                r.GeoTag = true;

                string photoName             = Path.GetFileNameWithoutExtension(threadInfo.File);
                string photoSQL              = "SELECT Photo_Geotag FROM PhotoList WHERE Photo_Camera = '" + photoName + "';";
                OleDbCommand commandGetPhoto = new OleDbCommand(photoSQL, connection);

                photo = (string)commandGetPhoto.ExecuteScalar();

                r.PhotoName                = photo; //new photo name
                string geotagSQL           = "UPDATE PhotoList SET PhotoList.GeoTag = True WHERE Photo_Camera = '" + photoName + "';";
                OleDbCommand commandGeoTag = new OleDbCommand(geotagSQL, connection);

                commandGeoTag.ExecuteNonQuery();
                path                     = outPath + "\\" + photo + ".jpg";
                string pathSQL           = "UPDATE PhotoList SET Path = '" + path + "' WHERE Photo_Camera = '" + photoName + "';";
                OleDbCommand commandPath = new OleDbCommand(pathSQL, connection);
                commandPath.ExecuteNonQuery();
                r.Path = path;
                lock (obj)
                {
                    geoTagCount++;

                    setMinMax(r.Latitude, r.Longitude);
                    int totalCount = geoTagCount + errorCount;
                    double percent = ((double)totalCount / length) * 100;
                    int percentInt = (int)Math.Floor(percent);
                    progressValue  = threadInfo.ProgressHandler;
                    progressForm.Invoke(
                        new MethodInvoker(() => progressValue.Report(percentInt)
                                          ));
                }
                await saveFile(image, path);
                image.Dispose();
                image = null;
            });

            return(r);
        }
        private Record readData(ThreadInfo threadInfo)
        {
            string outPath       = threadInfo.OutPath;
            int    length        = threadInfo.Length;
            string file          = Path.GetFullPath(threadInfo.File);
            string photo         = file;
            Image  image         = new Bitmap(file);
            Record r             = new Record(photo);
            var    progressValue = threadInfo.ProgressHandler as IProgress <int>;

            lock (obj)
            {
                id++;
            }
            PropertyItem[] propItems        = image.PropertyItems;
            PropertyItem   propItemLatRef   = image.GetPropertyItem(0x0001);
            PropertyItem   propItemLat      = image.GetPropertyItem(0x0002);
            PropertyItem   propItemLonRef   = image.GetPropertyItem(0x0003);
            PropertyItem   propItemLon      = image.GetPropertyItem(0x0004);
            PropertyItem   propItemAltRef   = image.GetPropertyItem(0x0005);
            PropertyItem   propItemAlt      = image.GetPropertyItem(0x0006);
            PropertyItem   propItemDateTime = image.GetPropertyItem(0x0132);

            image.Dispose();
            byte[]   latBytes      = propItemLat.Value;
            byte[]   latRefBytes   = propItemLatRef.Value;
            byte[]   lonBytes      = propItemLon.Value;
            byte[]   lonRefBytes   = propItemLonRef.Value;
            byte[]   altRefBytes   = propItemAltRef.Value;
            byte[]   altBytes      = propItemAlt.Value;
            byte[]   dateTimeBytes = propItemDateTime.Value;
            string   latitudeRef   = ASCIIEncoding.UTF8.GetString(latRefBytes);
            string   longitudeRef  = ASCIIEncoding.UTF8.GetString(lonRefBytes);
            string   altRef        = ASCIIEncoding.UTF8.GetString(altRefBytes);
            double   latitude      = byteToDegrees(latBytes);
            double   longitude     = byteToDegrees(lonBytes);
            double   altitude      = byteToDecimal(altBytes);
            DateTime dateTime      = byteToDate(dateTimeBytes);

            if (latitudeRef.Equals("S\0"))
            {
                latitude = -latitude;
            }
            if (longitudeRef.Equals("W\0"))
            {
                longitude = -longitude;
            }
            if (!altRef.Equals("\0"))
            {
                altitude = -altitude;
            }
            r.Latitude  = latitude;
            r.Longitude = longitude;
            r.Altitude  = altitude;
            r.TimeStamp = dateTime;
            r.Path      = Path.GetFullPath(file);
            r.Id        = id.ToString();

            lock (obj)
            {
                addRecord(photo, r);
                geoTagCount++;
                setMinMax(latitude, longitude);
                double percent    = ((double)geoTagCount / length) * 100;
                int    percentInt = (int)(Math.Round(percent));
                if (progressValue != null)
                {
                    progressValue = threadInfo.ProgressHandler;
                    progressForm.Invoke(
                        new MethodInvoker(() => progressValue.Report(percentInt)
                                          ));
                }
            }
            return(r);
        }
        public async Task <Dictionary <string, Record> > writeGeoTag(Dictionary <string, Record> recordDict, BlockingCollection <string> fileQueue, string inPath, string outPath)
        {
            int mQueueSize = fileQueue.Count;

            progressForm = new ProgressForm("Writing geotags to photos...");
            string[] _files = Directory.GetFiles(inPath);
            progressForm.Show();
            progressForm.BringToFront();
            progressForm.cancel += cancelImport;
            cts = new CancellationTokenSource();
            var token            = cts.Token;
            var progressHandler1 = new Progress <int>(value =>
            {
                progressForm.ProgressValue = value;
                progressForm.Message       = "Geotagging, please wait... " + value.ToString() + "% completed\n" +
                                             geoTagCount + " of " + mQueueSize + " photos geotagged\n" +
                                             "Photos with no geomark: " + stationaryCount + "\n" + "Photos with no gps point: " + errorCount + "\n";
            });
            var progressValue = progressHandler1 as IProgress <int>;

            geoTagCount     = 0;
            errorCount      = 0;
            stationaryCount = 0;
            Dictionary <string, Record> newRecordDict = new Dictionary <string, Record>();

            int processors       = Environment.ProcessorCount;
            int minWorkerThreads = processors;
            int minIOThreads     = processors;
            int maxWorkerThreads = processors;
            int maxIOThreads     = processors;

            //ThreadPool.SetMinThreads(minWorkerThreads, minIOThreads);
            ThreadPool.SetMaxThreads(maxWorkerThreads, maxIOThreads);
            await Task.Factory.StartNew(() =>
            {
                try
                {
                    while (fileQueue.Count != 0)
                    {
                        if (token.IsCancellationRequested)
                        {
                            token.ThrowIfCancellationRequested();
                        }
                        Parallel.Invoke(
                            () =>
                        {
                            ThreadInfo threadInfo      = new ThreadInfo();
                            threadInfo.OutPath         = outPath;
                            threadInfo.Length          = mQueueSize;
                            threadInfo.ProgressHandler = progressHandler1;
                            threadInfo.File            = fileQueue.Take();
                            try
                            {
                                Record r          = recordDict[Path.GetFileNameWithoutExtension(threadInfo.File)];
                                threadInfo.Record = r;

                                if (r.GeoMark)
                                {
                                    Record newRecord = null;
                                    newRecord        = ProcessFile(threadInfo).Result;
                                    newRecordDict.Add(r.PhotoName, r);
                                }
                                else
                                {
                                    object a = "nogps";
                                    incrementGeoTagError(a);
                                }
                            }
                            catch (KeyNotFoundException ex)
                            {
                                object a = "nokey";
                                incrementGeoTagError(a);
                            }
                        });
                    }
                }
                catch (OperationCanceledException)
                {
                    cts.Dispose();
                }
            }, cts.Token);

            progressForm.Invoke(
                new MethodInvoker(() => progressValue.Report(100)
                                  ));
            progressForm.enableOK();
            progressForm.disableCancel();
            return(newRecordDict);
        }
        public async Task <GMapOverlay> readGeoTag(BlockingCollection <string> fileQueue, string folderPath, string layer, string color)
        {
            int mQueueSize = fileQueue.Count;

            string[]    files   = Directory.GetFiles(folderPath);
            GMapOverlay overlay = new GMapOverlay(layer);

            progressForm = new ProgressForm("Importing Photos...");
            progressForm.Show();
            progressForm.BringToFront();
            progressForm.cancel += cancelImport;
            cts = new CancellationTokenSource();
            var token = cts.Token;

            geoTagCount = 0;
            id          = 0;

            int    length = files.Length;
            Bitmap bitmap = ColorTable.getBitmap(color, 4);
            Dictionary <string, Record> recordDict = new Dictionary <string, Record>();
            var progressHandler1 = new Progress <int>(value =>
            {
                progressForm.ProgressValue = value;
                progressForm.Message       = "Import in progress, please wait... " + value.ToString() + "% completed\n" +
                                             geoTagCount + " of " + mQueueSize + " photos geotagged";
            });
            var progressValue = progressHandler1 as IProgress <int>;
            await Task.Factory.StartNew(() =>
            {
                try
                {
                    while (fileQueue.Count != 0)
                    {
                        if (token.IsCancellationRequested)
                        {
                            token.ThrowIfCancellationRequested();
                        }
                        Parallel.Invoke(
                            () =>
                        {
                            ThreadInfo threadInfo      = new ThreadInfo();
                            threadInfo.Length          = mQueueSize;
                            threadInfo.ProgressHandler = progressHandler1;
                            threadInfo.File            = fileQueue.Take();

                            Record r = null;
                            r        = readData(threadInfo);

                            MarkerTag tag     = new MarkerTag(color, id);
                            GMapMarker marker = new GMarkerGoogle(new PointLatLng(r.Latitude, r.Longitude), bitmap);
                            marker.Tag        = tag;
                            tag.Size          = 4;
                            tag.PhotoName     = r.PhotoName;
                            tag.Path          = Path.GetFullPath(r.Path);
                            overlay.Markers.Add(marker);
                        });
                    }
                }
                catch (OperationCanceledException)
                {
                    cts.Dispose();
                }
            }, cts.Token);

            progressForm.Close();
            return(overlay);
        }