using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Collections;

using ESRI.ArcGIS.CatalogUI;
using ESRI.ArcGIS.Catalog;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geoprocessing;
//using ESRI.ArcGIS.Carto;
//using ESRI.ArcGIS.Display;
//using System.Drawing;
//using ESRI.ArcGIS.ADF.COMSupport;

/**
*	@file FunGDB.cs
*	Clase con funciones de tratamiento de la GDB
*/

/**
*	Clase con funciones de tratamiento de la GDB
*/
namespace OliviaAddIn
{
    public static class FunGDB
    {
        //**********************************************************************
        
        //**********************************************************************
        //Variables

        //**********************************************************************
        //Funciones
        /**
         * Lanza browser tipo catalog para seleccionar feature class
         * Devuelve el path completo
         */
        public static string selec_ftcls(String title, int parentWnd, string ini_loc = null)
        {
            string path = null;
            IGxDialog gxdlg;
            IEnumGxObject workspaces;
            IGxObject gxobj = null;

            gxdlg = new GxDialogClass();
            gxdlg.ObjectFilter = new GxFilterFeatureClasses();
            gxdlg.AllowMultiSelect = false;
            gxdlg.Title = title;
            gxdlg.ButtonCaption = "OK";
            if (ini_loc != null)
            {
                gxdlg.set_StartingLocation(System.IO.Path.GetDirectoryName(ini_loc));
                gxdlg.Name = System.IO.Path.GetFileNameWithoutExtension(ini_loc);
            }
            if (gxdlg.DoModalOpen(parentWnd, out workspaces) && workspaces != null)
            {
                gxobj = workspaces.Next();
                path = gxobj.FullName; 
            }
            else
            {
                path = null;
            }
            gxdlg.InternalCatalog.Close();
            libera(workspaces);
            libera(gxdlg);

            return path;
        }

        /**
         * Lanza browser tipo catalog para guardar feature class
         * Devuelve el path completo
         */
        public static string save_ftcls(string title, int parentWnd, string name_ini, out bool replacing)
        {
            string path = null;
            IGxDialog gxdlg;

            replacing = false;

            gxdlg = new GxDialogClass();
            gxdlg.ObjectFilter = new GxFilterFeatureClasses();
            gxdlg.AllowMultiSelect = false;
            gxdlg.Title = title;
            gxdlg.ButtonCaption = "OK";
            gxdlg.set_StartingLocation(System.IO.Path.GetDirectoryName(name_ini));
            gxdlg.Name=System.IO.Path.GetFileNameWithoutExtension(name_ini);
            if (gxdlg.DoModalSave(parentWnd))
            {
                path = gxdlg.Name;
            }
            else
            {
                path = null;
            }
            replacing = gxdlg.ReplacingObject;
            gxdlg.InternalCatalog.Close();
            libera(gxdlg);

            return path;
        }

        /**
         * Lanza browser tipo catalog para seleccionar gdb
         */
        public static IWorkspace selec_gdb(string title, int parentWnd, string reintentar_text = null)
        {
            IGxObject gxobj;
            IGxDialog gxdlg;
            IGxDatabase gxdb;
            IEnumGxObject workspaces;
            IWorkspace ws = null;

            gxdlg = new GxDialogClass();
            gxdlg.ObjectFilter = new GxFilterFileGeodatabasesClass();
            gxdlg.AllowMultiSelect = false;
            gxdlg.Title = title;
            gxdlg.ButtonCaption = "OK";
            if (gxdlg.DoModalOpen(parentWnd, out workspaces) && workspaces != null)
            {
                gxobj = workspaces.Next();

                if (gxobj is IGxDatabase)
                {
                    gxdb = (IGxDatabase)gxobj;
                    if (gxdb != null)
                        ws = gxdb.Workspace;
                }
                libera(workspaces);
            }
            else if (reintentar_text!=null)
            {
                if (DialogResult.Cancel == MessageBox.Show(reintentar_text, "Olivia", MessageBoxButtons.OKCancel, MessageBoxIcon.Question))
                {
                    ws = selec_gdb(title, parentWnd);
                }
                else
                    return null;
            }
            
            gxdlg.InternalCatalog.Close();
            libera(gxdlg);
            return ws;
        }

        /**
         * Devuelve el Feature Workspace dado el path de la clase o del directorio
         */
        public static IFeatureWorkspace abre_ftws(string clase_path)
        {
            IWorkspace ws;

            try
            {              

                ws = abre_ws(clase_path);
                if (ws == null)
                    return null;

                IFeatureWorkspace feat_ws = (IFeatureWorkspace)ws;

                return feat_ws;                
            }
            catch (Exception)
            {
                return null;
            }
        }

        /**
         * Devuelve el IWorkspaceName dado el path de la clase o del directorio
         */
        public static IWorkspaceName abre_wsn(string clase_path)
        {
            IWorkspace ws;
            IDataset data;
            IName name;
            IWorkspaceName wsn;


            try
            {
                ws = abre_ws(clase_path);
                if (ws == null)
                    return null;

                data = (IDataset)ws;
                name = data.FullName;
                wsn = (IWorkspaceName)name;

                return wsn;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /**
         * Devuelve el IWorkspace dado el path de la clase o del directorio
         */
        public static IWorkspace abre_ws(string clase_path)
        {
            IWorkspaceFactory wsf;
            IWorkspace ws;
            GPUtilities gp = new GPUtilities();
            string dir = null;

            try
            {
                IGxObject obj;

                /*
                //Información
                Category = "File Geodatabase Feature Class"
                obj = (IGxObject)gp.GetGxObjectFromLocation("C:\\Olivia\\gdbs\\BASE DE DATOS MADRE.gdb\\Comun\\c_Instalaciones");
                Category = "Shapefile"
                obj = (IGxObject)gp.GetGxObjectFromLocation("c:\\Olivia\\otros_data\\data_F01_C00_20180430_154828_I.shp");
                Category = "File Geodatabase"
                obj = (IGxObject)gp.GetGxObjectFromLocation("C:\\Olivia\\gdbs\\BASE DE DATOS MADRE.gdb");
                Category = "File Geodatabase Feature Dataset"
                obj = (IGxObject)gp.GetGxObjectFromLocation("C:\\Olivia\\gdbs\\BASE DE DATOS MADRE.gdb\\Comun");
                 * */

                obj = (IGxObject)gp.GetGxObjectFromLocation(clase_path);
                if (obj.Category == "Shapefile") //clase_path=c://archivos..//capa.shp, por ejemplo
                {
                    wsf = new ShapefileWorkspaceFactoryClass();
                    dir = obj.Parent.FullName;

                }
                else if (obj.Category == "File Geodatabase Feature Class")//clase_path=c://archivos..//datos.gdb//capa, por ejemplo
                {
                    wsf = new FileGDBWorkspaceFactoryClass();
                    if (obj.Parent.Category == "File Geodatabase Feature Dataset")//mira si está en un dataset
                        dir = obj.Parent.Parent.FullName;
                    else if (obj.Parent.Category == "File Geodatabase")
                        dir = obj.Parent.FullName;
                }
                else if (obj.Category == "File Geodatabase")//clase_path=c://archivos..//datos.gdb, por ejemplo
                {
                    wsf = new FileGDBWorkspaceFactoryClass();
                    dir = obj.FullName;
                }
                else if (obj.Category.Contains("Folder"))//clase_path=c://archivos.., por ejemplo
                {
                    wsf = new ShapefileWorkspaceFactoryClass();
                    dir = obj.FullName;
                }
                else
                    return null;

                if (dir == null)
                    return null;

                ws = wsf.OpenFromFile(dir, 0);

                return ws;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /**
         * Dado el path completo dels shape a crear, devuelve el IFeatureClassName del shape
         */
        public static IFeatureClassName crea_shp(string dir, string nomb)
        {
            IDatasetName shp_dsn = null;
            IWorkspaceName shp_wsn = null;
            IFeatureClassName shp_ftcn = null;
            try
            {
                shp_ftcn = new FeatureClassNameClass();
                shp_dsn = (IDatasetName)shp_ftcn;
                shp_dsn.Name = nomb;
                shp_wsn = abre_wsn(dir);
                if (shp_wsn == null)
                    return null;
                shp_dsn.WorkspaceName = shp_wsn;
            }
            catch (Exception)
            {
                return null;
            }
            return shp_ftcn;
        }

        /**
         * Devuelve la Feature Class dado el path de la clase
         * Hay que liberarla después de usarla
         */
        public static IFeatureClass abre_ftclass(string clase_path)
        {
            IFeatureWorkspace feat_ws = abre_ftws(clase_path);

            if (feat_ws == null)
                return null;

            IFeatureClass ftclass;
            try
            {
                ftclass = feat_ws.OpenFeatureClass(System.IO.Path.GetFileNameWithoutExtension(clase_path));
            }
            catch (Exception)
            {
                return null;
            }
            return ftclass;
        }

        /**
         * Devuelve la Feature Class dado el path de la gdb y el nombre de la clase
         * Hay que liberarla después de usarla
         */
        public static IFeatureClass abre_ftclass(string gdb_path, string ftclass_st)
        {
            IFeatureWorkspace feat_ws = abre_ftws(gdb_path);

            if (feat_ws == null)
                return null;

            IFeatureClass ftclass;
            try
            {
                ftclass = feat_ws.OpenFeatureClass(ftclass_st);
            }
            catch (Exception)
            {
                return null;
            }
            return ftclass;
        }

        /**
         * Devuelve la tabla de campos de una clase dado su path
         * Hay que liberarla después de usarla
         */
        public static ITable dame_tabla_clase(string clase_path)
        {
            ITable tabla;

            IFeatureWorkspace feat_ws = abre_ftws(clase_path);
            if (feat_ws == null)
                return null;

            try
            {
                tabla = feat_ws.OpenTable(System.IO.Path.GetFileNameWithoutExtension(clase_path));
            }
            catch (Exception)
            {
                return null;
            }
            
            return tabla;
        }

        /**
         * Devuelve la tabla de campos de una clase dado el nombre y la gdb que la contiene
         * Hay que liberarla después de usarla
         */
        public static ITable dame_tabla_clase(string gdb_path, string ftclass)
        {
            ITable tabla;

            IFeatureWorkspace feat_ws = abre_ftws(gdb_path);
            if (feat_ws == null)
                return null;

            try
            {
                tabla = feat_ws.OpenTable(ftclass);
            }
            catch (Exception)
            {
                return null;
            }

            return tabla;
        }       

        /*
         * Dado un path de clase y una consulta devuelve cuántas filas contienen datos
         */
        public static int cuenta_filas_ftclass(string clase_path, string consulta)
        {
            int f = -1;
            IQueryFilter filter = null;
            ITable tabla = null;

            try
            {
                //se abre la tabla
                tabla = dame_tabla_clase(clase_path);

                if (tabla == null)
                    return -1;

                //se aplica filtro
                filter = new QueryFilterClass();
                filter.WhereClause = consulta;

                f = tabla.RowCount(filter);
                return f;
            }
            catch (Exception)
            {
                return -1;
            }
            finally
            {
                libera(filter);
                libera(tabla);
            }
        }

        /*
         * Dado el nombre de una clase y la gdb que la contiene, y una consulta devuelve cuántas filas contienen datos
         */
        public static int cuenta_filas_ftclass(string gdb_path, string ftclass, string consulta)
        {
            int f = -1;
            IQueryFilter filter = null;
            ITable tabla = null;

            try
            {
                //se abre la tabla
                tabla = dame_tabla_clase(gdb_path, ftclass);

                if (tabla == null)
                    return -1;

                //se aplica filtro
                filter = new QueryFilterClass();
                filter.WhereClause = consulta;

                f = tabla.RowCount(filter);
                return f;
            }
            catch (Exception)
            {
                return -1;
            }
            finally
            {
                libera(filter);
                libera(tabla);
            }
        }

        /*
         * Devuelve los objectid de los elementos de clase_path que cumplan la consulta
         */
        public static int[] dame_ids_consulta(string clase_path, string consulta, IFeatureClass ftclass_=null)
        {
            IQueryFilter filtro_lin = null;
            ISelectionSet selectionSet = null;
            IEnumIDs enumIDs = null;
            IWorkspace ws = null;
            IFeatureWorkspace ft_ws = null;
            int n = 0, aux = 0, id = 0;
            int[] ids=null;
            IFeatureClass ftclass = null;

            //se cuenta el numero de filas (campos) que existen en la tabla.
            try
            {
                if (ftclass_ == null)
                {
                    ft_ws = abre_ftws(clase_path);
                    if (ft_ws == null)
                        return null;
                    ws = (IWorkspace)ft_ws;
                    ftclass = abre_ftclass(clase_path);
                    if (ftclass == null)
                        return null;
                }
                else
                    ftclass = ftclass_;

                filtro_lin = new QueryFilterClass();
                filtro_lin.WhereClause = consulta;

                //se hace una seleccion de todos los elementos que conforman la capa de entrada. 
                selectionSet = ftclass.Select(filtro_lin, esriSelectionType.esriSelectionTypeIDSet,
                    esriSelectionOption.esriSelectionOptionNormal, ws);
                n = selectionSet.Count;
                enumIDs = selectionSet.IDs;
                //Tiene en enumIDS los objectid de los elementos que cumplen el filtro, lo pasa a un array de enteros
                ids = new int[n];
                aux = 0;
                id = enumIDs.Next();
                //se hace una rray de enteros conformado por los IDs
                while (id != -1) //-1 es devuelto después del último ID válido de enumIDs(que es un array con los IDs de los elementos seleccionados)
                {
                    ids[aux] = id;
                    aux++;
                    id = enumIDs.Next();
                }
                return ids;
            }
            catch (Exception)
            {
                return null;
            }
            finally
            {
                libera(filtro_lin);
                if (ftclass_ == null)
                {
                    libera(selectionSet);
                    libera(ftclass);
                }
            }
        }

        /**
         * Devuelve los campos de una clase
         */
        public static string[] dame_campos_clase(string clase_path, out int err)
        {
            ITable tabla;
            IFields campos;
            int ncampos;
            string[] elems = null;
            err = 0;

            tabla = FunGDB.dame_tabla_clase(clase_path);
            if (tabla == null)
            {
                err = 1;
                return null;
            }

            try
            {
                campos = tabla.Fields;
                ncampos = campos.FieldCount;
            }
            catch (Exception)
            {
                err = 2;
                return null;
            }

            if (ncampos <= 0)
            {
                err = 3;
                return null;
            }
            elems = new string[ncampos];
            try
            {
                //se leen los campos de texto que contiene la capa para mostrarlos en la lista
                for (int i = 0; i < ncampos; i++)
                {
                    elems[i] = campos.get_Field(i).Name;
                }
                return elems;
            }
            catch (Exception)
            {
                err = 4;
                return null;
            }
            finally
            {
                libera(tabla);
            }
        }

        /*
         * Devuelve los atributos de un campo determinado de una tabla y se copian en un array string
         */
        public static string[] dame_valores_consulta(string clase_path, string campo, string consulta, out int err)
        {
            int[] ids=null;
            IRow row=null;
            ITable tabla = null;
            int n=0;
            string[] valores = null;
            err = 0;

            try
            {
                ids = dame_ids_consulta(clase_path, consulta);
                if (ids == null)
                {
                    err = 1;
                    return null;
                }

                n = ids.Length;
                if (n == 0)
                {
                    err = 2;
                    return null;
                }

                tabla = dame_tabla_clase(clase_path);
                if (tabla == null)
                {
                    err = 3;
                    return null;
                }

                valores = new string[n];
                for (int i = 0; i < n; i++)
                {
                    row = tabla.GetRow(ids[i]);
                    valores[i] = (row.get_Value(row.Fields.FindField(campo))).ToString();
                    libera(row);
                }


                return valores;
            }
            catch (Exception)
            {
                err = 4;
                return null;
            }
            finally
            {
                libera(tabla);
            }
        }

        /*
         * Devuelve los valores únicos de un campo determinado de una tabla y se copian en un array string
         */
        public static string[] dame_valunic_consulta(string clase_path, string campo, string consulta, out int err)
        {
            ITable tabla = null;
            int n = 0;
            string[] valores = null;
            IDataStatistics dataStats = null;
            ICursor cursor = null;
            IEnumerator ienum=null;
            object ob;

            err = 0;

            try
            {       
                tabla = dame_tabla_clase(clase_path);
                if (tabla == null)
                {
                    err = 2;
                    return null;
                }

                cursor = tabla.Search(null, false);
                dataStats = new ESRI.ArcGIS.Geodatabase.DataStatisticsClass();
                dataStats.Field = campo;
                dataStats.Cursor = cursor;

                ienum = dataStats.UniqueValues;
                List<string> val_list = new List<string>();
                ienum.MoveNext();
                ob = ienum.Current;
                while (ob!=null)
                {
                    val_list.Add((string)ob);
                    ienum.MoveNext();
                    ob = ienum.Current;
                    n++;
                }

                if (n > 0)
                {
                    valores = new string[n];
                    val_list.CopyTo(valores, 0);
                }
                else
                {
                    err = 3;
                }
                
                return valores;
            }
            catch (Exception)
            {
                err = 4;
                return null;
            }
            finally
            {
                libera(tabla);
                libera(cursor);
            }
        }

        /**
         * Libera el objeto
         */
        public static IGeometry reproyecta(IGeometry geom, int coorsys = (int)esriSRProjCS4Type.esriSRProjCS_ETRS1989_UTM_Zone_30N)
        {

            try
            {
                IGeometry geom_sal;
                ISpatialReference spatialReference;
                IProjectedCoordinateSystem coord_proy;
                ISpatialReferenceFactory3 spatialReferenceFactory;
                spatialReferenceFactory = (ISpatialReferenceFactory3)new SpatialReferenceEnvironment();
                coord_proy = spatialReferenceFactory.CreateProjectedCoordinateSystem(coorsys);//ETRS89_UTM30N 25830
                spatialReference = (ISpatialReference)coord_proy;
                geom.Project(spatialReference);
                geom_sal = geom;
                return geom_sal;
            }
            catch
            {
                return null;
            }

        }

        /*
         * Devuelve la geometría de modificando las coordendas de geograficas a proyectadas WGS84 UTM 30
         */
        public static IGeometryDef dame_geom_coords(IFeatureClass fc, int coorsys = (int)esriSRProjCS4Type.esriSRProjCS_ETRS1989_UTM_Zone_30N)//ETRS89_UTM30N 25830
        {
            int ind;
            IFields campos;
            ISpatialReferenceFactory3 spatialReferenceFactory;
            ISpatialReference spatialReference;
            String shapeFieldName;
            IField shapeField;
            IGeometryDef geom_def, dest_geom;
            IClone geom_clon, dest_clon;
            IGeometryDefEdit geom_edit;
            IProjectedCoordinateSystem coord_proy;

            try
            {
                campos = fc.Fields;
                //se genera un sistema de referencia espacial: de coordenadas proyectadas a coorsys
                spatialReferenceFactory = (ISpatialReferenceFactory3)new SpatialReferenceEnvironment();
                coord_proy = spatialReferenceFactory.CreateProjectedCoordinateSystem(coorsys);
                spatialReference = (ISpatialReference)coord_proy;

                //se consigue la forma (shape) de la featureclass de interés
                shapeFieldName = fc.ShapeFieldName;
                ind = fc.FindField(shapeFieldName);
                shapeField = campos.get_Field(ind);
                //conseguir la geometria definida por el campo shape
                geom_def = shapeField.GeometryDef;
                geom_clon = (IClone)geom_def;
                dest_clon = geom_clon.Clone();
                dest_geom = (IGeometryDef)dest_clon;
                geom_edit = (IGeometryDefEdit)dest_geom;
                /*if(geom_edit.SpatialReference.FactoryCode==0)
                {
                    //si no tiene info de proyección, se le dice que viene de coorsys (utm30n)
                    ISpatialReferenceFactory3 spatialReferenceFactory_aux = (ISpatialReferenceFactory3)new SpatialReferenceEnvironment();
                    IProjectedCoordinateSystem coord_proy_aux = spatialReferenceFactory.CreateProjectedCoordinateSystem(OliviaGlob.coorsys);
                    ISpatialReference spatialReference_aux = (ISpatialReference)coord_proy_aux;
                    geom_edit.SpatialReference_2 = spatialReference_aux;
                }*/
                geom_edit.SpatialReference_2 = spatialReference;//se modifica el sistema de referencia de coordenadas al generado esriSRProjCS_WGS1984UTM_30N               
            }
            catch (Exception)
            {
                return null;
            }

            return dest_geom;//esta clase se pasará como argumento a la hora de la exportacion. De modo que realizará el cambio de coordendas oportuno
        }

        /**
         * Libera el objeto
         */
        public static void libera(object ob)
        {
            if (ob != null)
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(ob);
            ob = null;
        }

        /**
        * Une las geometrías geom1 y geom2
        */
        public static IGeometry une_geoms(IGeometry geom1, IGeometry geom2)
        {
            ITopologicalOperator union;
            IGeometry geom = null;

            try
            {
                if (geom1 != null)
                {
                    union = (ITopologicalOperator)geom1;
                    //'union' contiene a geom1
                    if (geom2 != null)
                        geom = union.Union(geom2); 
                    else
                        geom = geom1;
                }
                else if (geom2 != null)
                    geom = geom2;
            }
            catch (Exception)
            {
                return null;
            }
            return geom;
        }

        /**
        * Realiza la intersección entre las dos geometrías que se pasan como argumentos.
         * Le quita geom_quita a geom_tot
        */
        public static IGeometry intersec_geoms(IGeometry geom1, IGeometry geom2)
        {
            ITopologicalOperator union;
            IGeometry geom = null;

            try
            {
                if (geom1 != null)
                {
                    union = (ITopologicalOperator)geom1;
                    //'union' contiene a geom1
                    if (geom2 != null)
                        geom = union.Intersect(geom2, esriGeometryDimension.esriGeometry2Dimension);
                    else
                        geom = geom1;
                }
                else if (geom2 != null)
                    geom = geom2;
            }
            catch (Exception)
            {
                return null;
            }
            return geom;
        }

        /**
         * Realiza la diferencia simétrica entre las dos geometrías que se pasan como argumentos.
         * Le quita geom_quita a geom_tot
        */
        public static IGeometry diferencia_geoms(IGeometry geom_tot, IGeometry geom_quita)
        {
            ITopologicalOperator union;
            IGeometry geom = null;

            try
            {
                if (geom_tot != null)
                {
                    union = (ITopologicalOperator)geom_tot;
                    //'union' contiene a geom1
                    if (geom_quita != null)
                        geom = union.Difference(geom_quita);
                    else
                        geom = geom_tot;
                }
                else if (geom_quita != null)
                    geom = geom_quita;
            }
            catch (Exception)
            {
                return null;
            }
            return geom;
        }

        /**
         * Forma el envelope que contiene los ámbitos para exportar, a partir de ahí la red navegable ampliando a un buffer
         * Envelope es un rectángulo, no es el polígono mínimo que los contiene
         */
        public static IGeometry dame_geom_envelope(string path_clase)
        {

            IFeatureClass capa;
            IGeoDataset gds;
            Polygon poli;
            ISegmentCollection segcol;
            IGeometry geom_envelope = null;

            try
            {
                capa = abre_ftclass(path_clase);
                if (capa == null)
                {
                    return null;
                }
                //castea la capa a IGeoDataset, de donde obtendrá el envelope
                gds = (IGeoDataset)capa;
                //crea un polígono partiendo del rectángulo envelope
                poli = new PolygonClass();
                segcol = (ISegmentCollection)poli;
                segcol.SetRectangle(gds.Extent);

                geom_envelope = (IGeometry)poli;
                //libera
                FunGDB.libera(capa);

                return geom_envelope;
            }
            catch
            {
                return null;
            }
        }

        /*
         * A partir de una capa recorre todos los elementos que cumplen el filtro y los une en una única geometría
         */
        public static IGeometry une_geometria(IFeatureClass fc1, IQueryFilter filtro, out string err_st)
        {
            IFeatureCursor fcc = null;
            IFeature f1 = null, f2 = null;
            IGeometry queryGeometry = null;
            ITopologicalOperator union = null;

            err_st = "";
            try
            {
                fcc = fc1.Search(filtro, false);
            }
            catch (Exception)
            {
                err_st = string.Format("La capa {0} no contiene elementos que cumplan la consulta: {1}", fc1.AliasName, filtro.WhereClause);
                return null;
            }

            try
            {
                f2 = f1 = fcc.NextFeature();
                if (f1 == null)
                {
                    err_st = string.Format("La capa {0} no contiene elementos que cumplan la consulta: {1}", fc1.AliasName, filtro.WhereClause);
                    return null;
                }
                f2 = fcc.NextFeature();

                union = (ITopologicalOperator)f1.Shape;
                while (f1 != null && f2 != null)
                {
                    f1.Shape = union.Union(f2.Shape);
                    union = (ITopologicalOperator)f1.Shape;
                    f2.Delete();
                    FunGDB.libera(f2);
                    f2 = fcc.NextFeature();
                }

                queryGeometry = f1.Shape;
                f1.Store();

                return queryGeometry;

            }
            catch (Exception ex)
            {
                err_st = string.Format("Error al unir geometría de la capa {0} con el filtro {1}: {2}", fc1.AliasName, filtro.WhereClause, ex.Message);
                return null;
            }
            finally
            {
                //libera
                FunGDB.libera(f1);
                FunGDB.libera(f2);
                FunGDB.libera(fcc);
            }
        }  

        /**
         * Forma la envolvente compleja, el mínimo polígono,
         * que contiene los ámbitos para exportar, a partir de ahí la red navegable ampliando a un buffer
         */
        public static IGeometry dame_geom_convexhull(string path_clase)
        {

            IFeatureClass fc;
            IGeometry geom, geom_convhull;
            ITopologicalOperator convhull = null;
            IQueryFilter filtro;
            string err_st;

            try
            {
                filtro = new QueryFilterClass();
                filtro.WhereClause="";
                fc = abre_ftclass(path_clase);
                if (fc == null)
                {
                    return null;
                }
                geom = une_geometria(fc, filtro, out err_st);
                if (geom == null)
                {
                    return null;
                }
                //castea la geometría a itopologicaloperator
                convhull = (ITopologicalOperator)geom;
                geom_convhull = convhull.ConvexHull();

                //libera
                FunGDB.libera(fc);

                return geom_convhull;
            }
            catch
            {
                return null;
            }
        }

        /**
         * Amplia la geom para que contenga el punto p ampliando el rectangulo del envelope
         */
        public static IGeometry amplia_geom(IGeometry geom, double p_x, double p_y)
        {
            IEnvelope env = null;
            IPoint[] points;
            IPolygon poli = new PolygonClass();
            ISegmentCollection segcol;

            env = geom.Envelope;
            points = new IPoint[5];

            points[0] = env.LowerLeft;
            points[1] = env.LowerRight;
            points[2] = env.UpperLeft;
            points[3] = env.UpperRight;
            points[4] = new PointClass();
            points[4].PutCoords(p_x, p_y);

            IEnvelopeGEN envelopegen = new EnvelopeClass();            

            try
            {
                envelopegen.DefineFromPoints(ref points);

                poli = new PolygonClass();
                segcol = (ISegmentCollection)poli;
                segcol.SetRectangle((IEnvelope)envelopegen);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }

            geom = (IGeometry)poli;

            return geom;
        }

        /**
         * Amplia la geom para que contenga el punto p ampliando la envolvente convexa
         */
        public static IGeometry amplia_geom_convexhull(IGeometry geom, double p_x, double p_y)
        {
            IPoint p;
            ITopologicalOperator union;
            IGeometry geom_p,geom_tot, geom_convhull;
            IGeometry5 geom_p_;

            try
            {
                p = new Point();
                p.PutCoords(p_x, p_y);
                geom_p_ = (IGeometry5)p;
                geom_p_.SpatialReference = geom.SpatialReference;
                union = (ITopologicalOperator)geom_p_;
                geom_p = union.Buffer(100);
                union = (ITopologicalOperator)geom_p;
                //une el punto nuevo a la geometría geom
                geom_tot = union.Union(geom);

                //reapunta union al total
                union = (ITopologicalOperator)geom_tot;
                geom_convhull = union.ConvexHull();

                return geom_convhull;
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message);
                return null;
            }
        }

        /*
         * Añade a la capa de origen la columna que se pasa como argumento
         */
        public static bool add_campo(string path_clase, string nombre_campo, esriFieldType tipo_campo, out int err)
        {
            IFeatureClass fc = null;
            IFields oldFields = null, fields = null;
            IClone clone_fuente = null, clone_dest = null;
            IFieldsEdit fields_edit = null;
            ITable tabla = null;
            IField field = null;
            IFieldEdit field_edit = null;

            err = 0;

            try
            {

                fc = abre_ftclass(path_clase);
                if (fc == null)
                {
                    err = 1;
                    return false;
                }

                oldFields = fc.Fields;
                clone_fuente = (IClone)oldFields;
                clone_dest = clone_fuente.Clone();
                fields = (IFields)clone_dest;
                //se castea para poder enditar los campos de las tablas
                fields_edit = (IFieldsEdit)fields;
                tabla = dame_tabla_clase(path_clase);
                if (tabla == null)
                {
                    err = 2;
                    return false;
                }

                if (tabla.FindField(nombre_campo) > 0)
                {
                    err = 3;
                    return false;
                }

                //se añade un campo entero
                field = new FieldClass();
                field_edit = (IFieldEdit)field;
                field_edit.Name_2 = nombre_campo;
                field_edit.Type_2 = tipo_campo;
                //string_field_edit.Length_2 = 100;
                field_edit.Precision_2 = 38;
                field_edit.Scale_2 = 0;
                fc.AddField(field_edit);

                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                libera(fc);
                libera(tabla);
            }
        }

        /*
         * Exporta de la gdb src al shp dest
         */
        public static bool exporta(string dir_gdb_src, string nom_src, string dir_shp_dest, string nom_dest, IQueryFilter filtro, out IDatasetName shp_data_name, out string err_st)
        {
            string name = "";
            IFeatureClassName shp_fcn = null;
            IGeometryDef dest_geom = null;
            ITable tabla = null;
            int nfilas;
            ESRI.ArcGIS.GeoDatabaseUI.IExportOperation exportOperation;
            IFeatureClassName gdb_fcn;
            IDatasetName gdb_dsn;
            IWorkspaceName gdb_wsn;

            err_st = "";
            shp_data_name = null;
            
            //prepara el shp donde va a exportar
            shp_fcn = FunGDB.crea_shp(dir_shp_dest, nom_dest);
            if (shp_fcn == null)
            {
                err_st = "Error al crear el shape de exportación " + name;
                return false;
            }
            
            /////////////////////////////////////////////
            //obtiene la geomdef con las coordenadas proyectadas   
            IFeatureClass fc;
            if (nom_src == null)
                fc = abre_ftclass(dir_gdb_src);
            else
                fc = abre_ftclass(dir_gdb_src, nom_src);
            if (fc == null)
                return false;
            
            dest_geom = FunGDB.dame_geom_coords(fc, OliviaGlob.coorsys);
            if (dest_geom == null)
            {
                err_st = "Error al generar el sistema de coordendas proyectadas en la capa " + nom_src;
                return false;
            }
            FunGDB.libera(fc);

            //Inicia la gdb de donde va a exportar
            gdb_wsn = FunGDB.abre_wsn(dir_gdb_src);
            if (gdb_wsn == null)
            {
                err_st = "Error al abrir la gdb de exportación " + name;
                return false;
            }

            try
            {
                gdb_fcn = new FeatureClassNameClass();
                gdb_dsn = (IDatasetName)gdb_fcn;
                if (nom_src == null)
                    gdb_dsn.Name = System.IO.Path.GetFileNameWithoutExtension(dir_gdb_src);
                else
                    gdb_dsn.Name = nom_src;
                gdb_dsn.WorkspaceName = (IWorkspaceName)gdb_wsn;

                //Se prepara para exportar
                if (nom_src == null)
                    tabla = FunGDB.dame_tabla_clase(dir_gdb_src);
                else
                    tabla = FunGDB.dame_tabla_clase(dir_gdb_src, nom_src);
                if (tabla == null)
                {
                    err_st = "Error al abrir la tabla de exportación " + name;
                    return false;
                }
                nfilas = tabla.RowCount(filtro);
                if (nfilas <= 0)
                {   
                    err_st = string.Format("No hay elementos que cumplan la condición y se encuentren en el nivel y en la zona seleccionados");
                    return false;
                }
                //se prepara la exportación
                exportOperation = new ESRI.ArcGIS.GeoDatabaseUI.ExportOperationClass();
                exportOperation.ExportFeatureClass(gdb_dsn, filtro, null, dest_geom, shp_fcn, 0);
            }
            catch (Exception)
            {
                err_st = "Error al exportar el ámbito de trabajo con los datos introducidos";
                return false;
            }
            finally
            {
                FunGDB.libera(tabla);
            }

            shp_data_name = (IDatasetName)shp_fcn;
            return true;
        }

        /*
         * Devuelve las coordenadas de la feature class
         */
        public static int dame_factory_code(string dir_gdb_src, string nom_src)
        {
            IFeatureClass fc;
            int coor = -1;

            if (nom_src == null)
                fc = abre_ftclass(dir_gdb_src);
            else
                fc = abre_ftclass(dir_gdb_src, nom_src);
            if (fc == null)
                return coor;

            coor = ((IGeoDataset)fc).SpatialReference.FactoryCode;

            FunGDB.libera(fc);

            return coor;
        }

        /*
         * Devuelve los atributos de un campo determinado de una tabla y se copian en un array string
         */
        public static bool quita_filas(string shp_path, List<int> ids_filas)
        {
            ITable tabla = null;
            IRow row = null;
            try
            {
                //borra las líneas que se han indicado
                int offset = 0;

                tabla = FunGDB.dame_tabla_clase(shp_path);
                if (tabla == null)
                    return false;
                for (int j = 0; j < ids_filas.Count; j++)
                {
                    row = tabla.GetRow(ids_filas[j] - offset); //offset porque según va borrando se van desplazando las filas.
                    row.Delete();
                    offset++;
                    FunGDB.libera(row);
                }
            }
            catch
            {
                return false;
            }
            finally
            {
                FunGDB.libera(tabla);
                FunGDB.libera(row);
            }
            return true;
        }

        /**
         * Indica si el punto está en el polígono de la geometría
         */
        public static bool is_pto_in_geom(double x, double y, IGeometry geom_poly)
        {
            Point punto_ins = new Point();
            IGeometry geom_ins, geom;
            ITopologicalOperator union;

            try
            {
                punto_ins.PutCoords(x, y);
                geom_ins = (IGeometry)punto_ins;
                union = (ITopologicalOperator)geom_poly;
                geom = union.Intersect(geom_ins, esriGeometryDimension.esriGeometry0Dimension);

                if (!geom.IsEmpty)
                {
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }        

        /**
         * Borra la ftclass de la gdb para poder sobreescribir
         */
        public static bool borra_ftclass(string path_gdb, string ftclass_nomb)
        {
            IWorkspace2 ws_gdb_;
            IFeatureWorkspace fws;
            IDataset data_delete;

            ws_gdb_ = (IWorkspace2)FunGDB.abre_ws(path_gdb);
            if (ws_gdb_ == null)
                return false;
            fws = (IFeatureWorkspace)ws_gdb_;

            if (!ws_gdb_.get_NameExists(esriDatasetType.esriDTFeatureClass, ftclass_nomb))
                return true;//no existe, no hay nada que borrar

            data_delete = (IDataset)fws.OpenFeatureClass(ftclass_nomb);
            if ((data_delete != null) && data_delete.CanDelete())
            {
                try
                {
                    data_delete.Delete();
                }
                catch
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
            return true;
        }

        /**
         *Crea el feature dataset
         */
        public static bool crea_ftdataset(string path_gdb, string dataset_nomb, ISpatialReference spref)
        {
            IWorkspace2 ws_gdb_;
            IFeatureWorkspace fws;
            IFeatureDataset ft_dataset=null;

            try
            {

                ws_gdb_ = (IWorkspace2)FunGDB.abre_ws(path_gdb);
                if (ws_gdb_ == null)
                    return false;
                fws = (IFeatureWorkspace)ws_gdb_;

                if (ws_gdb_.get_NameExists(esriDatasetType.esriDTFeatureDataset, dataset_nomb))
                    return true;//existe, no hay nada que crear

                ft_dataset = fws.CreateFeatureDataset(dataset_nomb, spref);
                if (ft_dataset == null)
                {
                    return false;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /**
         * Indica si la capa contiene puntuales
         */
        public static bool is_ftclass_punt(IFeatureClass clase)
        {
            return clase.ShapeType == esriGeometryType.esriGeometryPoint ||
                clase.ShapeType == esriGeometryType.esriGeometryMultipoint;
        }

        /**
         * Indica si la capa contiene superficiales
         */
        public static bool is_ftclass_sup(IFeatureClass clase)
        {
            return clase.ShapeType == esriGeometryType.esriGeometryPolygon;
        }

        /**
         * Indica si la capa contiene lineales
         */
        public static bool is_ftclass_lin(IFeatureClass clase)
        {
            return clase.ShapeType == esriGeometryType.esriGeometryPolyline;
        }

        /**
         * Indica si el campo es string
         */
        public static bool is_str_field(string clase_path,string campo)
        {
            int ind=-1;
            ITable tabla = dame_tabla_clase(clase_path);
            if(tabla!=null)
                ind=tabla.FindField(campo);
            if (ind >= 0)
                return tabla.Fields.get_Field(ind).Type == esriFieldType.esriFieldTypeString;
            else
                return false;
        }

    }


}
