static void Main(string[] args)
        {
            //validate input
            /*if (args.Length != 2)
            {
                Console.WriteLine(string.Join(",", args));
                throw new Exception("Required arguments are: path/to/parent-directory-of-gis-data path/to/app-folder-of-quito-climate-study-project");
            }*/

            #if DEBUG

            string srcDir = @"C:\data\Geoportal_Test";// @"C:\dev\quito\For_Geoportal_WGS\For_Geoportal_WGS\";// args[0];// @"C:\dev\quito\For_Geoportal_WGS\For_Geoportal_WGS\";
            string resultDir = @"C:\data\result"; //  @"C:\dev\quito\buildtest"; //args[1];
            #else

            string srcDir = args[0].Trim(); // @"C:\dev\quito\For_Geoportal_WGS\For_Geoportal_WGS\";// args[0];// @"C:\dev\quito\For_Geoportal_WGS\For_Geoportal_WGS\";
            string resultDir = args[1].Trim(); //  @"C:\dev\quito\buildtest"; //args[1];
            #endif
            if (!srcDir.EndsWith("\\"))
            {
                srcDir += "\\";
            }
            if (!resultDir.EndsWith("\\"))
            {
                resultDir += "\\";
            }

            Console.WriteLine("\n");
            Console.WriteLine("Source:");
            Console.WriteLine(srcDir);
            Console.WriteLine("Destination:");
            Console.WriteLine(resultDir);

            //verify gdal dlls are available; see: http://trac.osgeo.org/gdal/wiki/GdalOgrCsharpUsage
            string GDAL_HOME = @";c:\Program Files (x86)\FWTools2.4.7\bin";
            string path = Environment.GetEnvironmentVariable("PATH");
            path += ";" + GDAL_HOME;
            SetEnvironmentVariable("PATH", path);

            //register gdal extensions
            Gdal.AllRegister();

            string copyToOut = @"";
            string rasterout = @"raster\";
            string vectorout = @"vector\";
            string legendout = @"legend\";

            string tifResultDir = resultDir + copyToOut + rasterout;
            string shpResultDir = resultDir + copyToOut + vectorout;
            DirectoryInfo copyTo = new DirectoryInfo(srcDir + copyToOut);
            if (!copyTo.Exists)
            {
                copyTo.Create();
            }

            DirectoryInfo legResultDir = new DirectoryInfo(resultDir + copyToOut + legendout);

            if (args.Length == 1)
            {
                srcDir = args[0];
            }

            IColorRepository colorRepo = new ColorRepository();
            LegendRepository legend = new LegendRepository();

            //see: http://sharpmap.codeplex.com/discussions/421752
            GeoAPI.GeometryServiceProvider.Instance = new NetTopologySuite.NtsGeometryServices();

            List<string> processedRasters = new List<string>();
            List<string> processedVectors = new List<string>();
            Stopwatch sw = new Stopwatch();
            sw.Start();

            Tuple<List<string>, List<string>> rasterResults = new Tuple<List<string>,List<string>>(new List<string>(), new List<string>());
            Tuple<List<string>, List<string>> gridResults = new Tuple<List<string>,List<string>>(new List<string>(), new List<string>());
            Tuple<List<string>, List<string>> vectorResults = new Tuple<List<string>,List<string>>(new List<string>(), new List<string>());
            //compute files to publish to web
            try
            {
                Console.WriteLine("Processing raster files...");
                rasterResults = ProcessDatasets(args, srcDir, tifResultDir, colorRepo, legend, ".tif", processedRasters);
                processedRasters = rasterResults.Item1;
            }
            catch (Exception rastEx)
            {
                Console.WriteLine("Exception processing raster files ");
                Console.WriteLine(rastEx.Message);
                Console.WriteLine(rastEx.InnerException.ToString());
            }

            try
            {
                Console.WriteLine("Processing grid files...");
                gridResults = ProcessDatasets(args, srcDir, tifResultDir, colorRepo, legend, "hdr.adf", processedRasters);
                processedRasters.AddRange(gridResults.Item1);
            }
            catch (Exception gridEx)
            {
                Console.WriteLine("Exception processing grid files ");
                Console.WriteLine(gridEx.Message);
                Console.WriteLine(gridEx.InnerException.ToString());
            }

            try
            {
                Console.WriteLine("Processing vector files...");
                vectorResults = ProcessDatasets(args, srcDir, shpResultDir, colorRepo, legend, ".shp", processedVectors);
                processedVectors = vectorResults.Item1;
            }
            catch (Exception vex)
            {
                Console.WriteLine("Exception processing vector files ");
                Console.WriteLine(vex.Message);
                Console.WriteLine(vex.InnerException.ToString());
            }

            //output legend file
            if (!legResultDir.Exists)
            {
                legResultDir.Create();
            }
            File.WriteAllText(legResultDir.FullName + "legend.json", JsonConvert.SerializeObject(legend));

            //reporting
            sw.Stop();
            Console.WriteLine("*********");
            Console.WriteLine("Unknown raster color maps in colormap.json, these files were not processed:");
            rasterResults.Item2.ForEach(a => Console.WriteLine("=> " + a));
            Console.WriteLine("Unknown vector color maps in colormap.json, these files were not processed:");
            vectorResults.Item2.ForEach(a => Console.WriteLine("=> " + a));
            Console.WriteLine("Finished processing {0} datasets in {1} seconds. Press any key to close.", processedRasters.Count() + processedVectors.Count(), sw.Elapsed.TotalSeconds);
            Console.ReadKey();
        }
        /// <summary>
        /// Applies color map to input 1-band 32-bit float raster files and persists results as new .tif files with equivalent spatial 
        /// metadata as respective source files as 3-band 8-bit int raster files suitable for web tiling.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="colorRepo"></param>
        /// <param name="extension"></param>
        /// <param name="di"></param>
        /// <param name="resultDir"></param>
        private static Tuple<List<string>, List<string>> ProcessRasterFiles(string[] args, IColorRepository colorRepo, LegendRepository legend, string extension, DirectoryInfo di, DirectoryInfo resultDir, List<string> processedDatasets)
        {
            OSGeo.GDAL.Driver srcDrv = Gdal.GetDriverByName("GTiff");
            List<string> skipped = new List<string>();
            foreach (FileInfo fi in FilesInDirectoryWithExtension(extension, di))
            {
                if (colorRepo.HasColorMappingOfFile(fi.Name))
                {
                    foreach (string resultName in colorRepo.ResultFileName(fi.Name))
                    {
                        if (!processedDatasets.Contains(resultName))
                        {
                            if (colorRepo.FileLegend(fi.Name, resultName).Any(a => string.IsNullOrEmpty(a.LegendFile)))
                            {
                                ProcessRasterFile(args, colorRepo, resultDir, srcDrv, fi, resultName);
                            }

                            string plainName = resultName.Replace(".tif", "").Replace(".json", "");
                            DirectoryInfo dirOut = new DirectoryInfo(resultDir + plainName);
                            if (!dirOut.Exists)
                            {
                                dirOut.Create();
                            }
                            legend.Add(plainName, colorRepo.FileLegend(fi.Name, resultName));
                            processedDatasets.Add(resultName);
                        }
                    }
                }
                else
                {
                    skipped.Add(fi.Name);
                }
            }
            return new Tuple<List<string>, List<string>>(processedDatasets, skipped);
        }
 /// <summary>
 /// Changes vector shapefiles into GeoJSON-formatted JSON objects, and csv files of the data in their attribute tables
 /// </summary>
 /// <param name="args"></param>
 /// <param name="colorRepo"></param>
 /// <param name="extension"></param>
 /// <param name="di"></param>
 /// <param name="resultDir"></param>
 /// <remarks>See http://nettopologysuite.googlecode.com/svn/branches/v2.0/NetTopologySuite.Samples.Console/SimpleTests/Attributes/AttributesTest.cs </remarks>
 private static Tuple<List<string>, List<string>> ProcessVectorFiles(string[] args, IColorRepository colorRepo, LegendRepository legend, string extension, DirectoryInfo di, DirectoryInfo resultDir, List<string> processedDatasets)
 {
     List<string> skipped = new List<string>();
     foreach (FileInfo fi in FilesInDirectoryWithExtension(extension, di))
     {
         if (colorRepo.HasColorMappingOfFile(fi.Name))
         {
             foreach (string resultName in colorRepo.ResultFileName(fi.Name))
             {
                 if (!processedDatasets.Contains(resultName))
                 {
                     ProcessVectorFile(colorRepo, resultDir, fi, resultName);
                     legend.Add(resultName, colorRepo.FileLegend(fi.Name, resultName));
                     processedDatasets.Add(resultName);
                 }
             }
         }
         else
         {
             skipped.Add(fi.Name);
         }
     }
     return new Tuple<List<string>, List<string>>(processedDatasets, skipped);
 }
        /// <summary>
        /// Applies required transformations to input GIS datasets for web publishing of study sources.
        /// </summary>
        /// <param name="args">Ag</param>
        /// <param name="srcDir"></param>
        /// <param name="resultDirectory"></param>
        /// <param name="colorRepo"></param>
        /// <param name="legendRepo"></param>
        /// <param name="fileSearchPattern">Locates files to process in a directory by matching * + this against filenames.</param>
        /// <param name="processedDatasets"></param>
        /// <returns></returns>
        private static Tuple<List<string>, List<string>> ProcessDatasets(string[] args, string srcDir, string resultDirectory, 
			IColorRepository colorRepo, LegendRepository legendRepo, string fileSearchPattern, List<string> processedDatasets)
        {
            if (!fileSearchPattern.Equals(".tif") && !fileSearchPattern.Equals(".shp") && !fileSearchPattern.Contains(".adf"))
            {
                throw new NotSupportedException("Only '.tif', '.shp' and 'hdr.adf' files are supported.");
            }
            DirectoryInfo di = new DirectoryInfo(srcDir);
            List<string> skipped = new List<string>();
            int filesToProcess = di.GetFiles("*" + fileSearchPattern).Count();
            Tuple<List<string>, List<string>> results;
            if (filesToProcess > 0)
            {
                DirectoryInfo resultDir = new DirectoryInfo(resultDirectory);
                if (!resultDir.Exists)
                {
                    resultDir.Create();
                }

                if (fileSearchPattern == ".tif")
                {
                    results = ProcessRasterFiles(args, colorRepo, legendRepo, fileSearchPattern, di, resultDir, processedDatasets);
                    processedDatasets = results.Item1;
                    skipped.AddRange(results.Item2);
                }
                else if (fileSearchPattern == ".shp")
                {
                    results = ProcessVectorFiles(args, colorRepo, legendRepo, fileSearchPattern, di, resultDir, processedDatasets);
                    processedDatasets = results.Item1;
                    skipped.AddRange(results.Item2);
                }
                else
                {
                    results = ProcessGridFiles(args, colorRepo, legendRepo, fileSearchPattern, di, resultDir, processedDatasets);
                    processedDatasets = results.Item1;
                    skipped.AddRange(results.Item2);
                }
            }

            //recurse through subdirectories
            foreach (DirectoryInfo subDi in di.GetDirectories())
            {
                results = ProcessDatasets(args, subDi.FullName, resultDirectory, colorRepo, legendRepo, fileSearchPattern, processedDatasets);
                processedDatasets = results.Item1;
                skipped.AddRange(results.Item2);
            }
            return new Tuple<List<string>,List<string>>(processedDatasets, skipped);
        }