From d9e768cc49d1914f4eebf32c08a93776cc5df669 Mon Sep 17 00:00:00 2001 From: mrpoup Date: Mon, 20 May 2019 23:03:22 +0200 Subject: [PATCH 01/10] Update StatsPopulationServices.cs --- .../Services/VisualisationServices/StatsPopulationServices.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DEM.Net.Core/Services/VisualisationServices/StatsPopulationServices.cs b/DEM.Net.Core/Services/VisualisationServices/StatsPopulationServices.cs index 267888de..363859d3 100644 --- a/DEM.Net.Core/Services/VisualisationServices/StatsPopulationServices.cs +++ b/DEM.Net.Core/Services/VisualisationServices/StatsPopulationServices.cs @@ -210,6 +210,7 @@ public Dictionary> GetClassesOrdonnees_parSeuilsDeValeur(Dicti } return v_classes; } + // public Dictionary GetSeuilBasClasses_memeEspaceInterclasse(int p_nbreClasses, double p_valeurMin, double p_valeurMax) { Dictionary v_seuilsBas = new Dictionary(); @@ -231,8 +232,7 @@ public Dictionary GetSeuilBasClasses_memeEspaceInterclasse(int p_nb throw; } return v_seuilsBas; - } - + } public Dictionary GetSeuilBasClasses_parIsoQuantite(Dictionary p_valeurParObjet, int p_nbreClasses) { Dictionary v_seuilsBas = new Dictionary(); From e1b51e1c904c87de5e9ca0b43d8a60662214b93a Mon Sep 17 00:00:00 2001 From: mrpoup Date: Tue, 11 Jun 2019 21:10:46 +0200 Subject: [PATCH 02/10] courves de niveau et cie --- .../Services/CourbesNiveau/BeanCourbes.cs | 34 + .../CourbesNiveau/BeanFractionCourbe.cs | 19 + .../BeanParametresCalculCourbesNiveau.cs | 30 + .../CourbesNiveau/BeanPointDecoup_internal.cs | 19 + .../CourbesNiveau/CourbesNiveauServices.cs | 991 ++++++++++++++++++ .../CourbesNiveau/ICourbesNiveauServices.cs | 38 + .../CourbesNiveau/enumCourbesNiveau.cs | 33 + DEM.Net.Core/Services/Lab/BeanArc_internal.cs | 6 +- .../Services/Lab/BeanPoint_internal.cs | 6 +- .../Services/Lab/BeanTopologieFacettes.cs | 2 +- .../Services/Lab/CalculServicesVoronoi.cs | 131 +++ .../Services/Lab/CalculServices_Medium.cs | 203 ++++ DEM.Net.Core/Services/Lab/FLabServices.cs | 4 + .../Services/Lab/ICalculServicesVoronoi.cs | 9 + .../Services/Lab/ICalculServices_Medium.cs | 5 +- .../Services/Lab/IUtilitairesServices.cs | 7 +- .../Services/Lab/UtilitairesServices.cs | 47 +- DEM.Net.Core/Services/Lab/Voronoi.cs | 985 +++++++++++++++++ DEM.Net.Core/Services/Lab/VoronoiElements.cs | 142 +++ DEM.Net.Core/Services/Lab/enumServices.cs | 6 + .../EchantillonsTestsServices.cs | 3 +- 21 files changed, 2678 insertions(+), 42 deletions(-) create mode 100644 DEM.Net.Core/Services/CourbesNiveau/BeanCourbes.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/BeanFractionCourbe.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/BeanParametresCalculCourbesNiveau.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/BeanPointDecoup_internal.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/CourbesNiveauServices.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/ICourbesNiveauServices.cs create mode 100644 DEM.Net.Core/Services/CourbesNiveau/enumCourbesNiveau.cs create mode 100644 DEM.Net.Core/Services/Lab/CalculServicesVoronoi.cs create mode 100644 DEM.Net.Core/Services/Lab/ICalculServicesVoronoi.cs create mode 100644 DEM.Net.Core/Services/Lab/Voronoi.cs create mode 100644 DEM.Net.Core/Services/Lab/VoronoiElements.cs diff --git a/DEM.Net.Core/Services/CourbesNiveau/BeanCourbes.cs b/DEM.Net.Core/Services/CourbesNiveau/BeanCourbes.cs new file mode 100644 index 00000000..485b8eae --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/BeanCourbes.cs @@ -0,0 +1,34 @@ +using DEM.Net.Core.Services.Lab; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public class BeanCourbes + { + public List p00_pointsAlti { get; set; } + public BeanParametresCalculCourbesNiveau p01_parametresCalculDesCourbes { get; set; } + // + public Dictionary p10_labelClassesParIndexClasse { get; set; } + public Dictionary p11_indexClasseParLabelClasse { get; set; } + public Dictionary> p12_tousSegmentsCourbesParNiveau { get; set; } + public List p13_arcsPeripherieToClosePolygones { get; set; } + + public Dictionary>> p14_courbesAssembleesCoordParNiveau { get; set; } + public BeanCourbes() + { + p00_pointsAlti = new List(); + p10_labelClassesParIndexClasse = new Dictionary(); + p11_indexClasseParLabelClasse = new Dictionary(); + p12_tousSegmentsCourbesParNiveau = new Dictionary>(); + p13_arcsPeripherieToClosePolygones = new List(); + p14_courbesAssembleesCoordParNiveau = new Dictionary>>(); + + } + + } + +} diff --git a/DEM.Net.Core/Services/CourbesNiveau/BeanFractionCourbe.cs b/DEM.Net.Core/Services/CourbesNiveau/BeanFractionCourbe.cs new file mode 100644 index 00000000..f47c9c62 --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/BeanFractionCourbe.cs @@ -0,0 +1,19 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public class BeanFractionCourbe + { + public string p00_codeFractionCourbe { get; set; } + public BeanPointDecoup_internal p01_point_1 { get; set; } + public BeanPointDecoup_internal p02_point_2 { get; set; } + //public SqlGeometry p10_geom { get; set; } + public string p20_valeurCourbe { get; set; } + public bool p11_estLigneSinonPoint_vf { get; set; } + } +} diff --git a/DEM.Net.Core/Services/CourbesNiveau/BeanParametresCalculCourbesNiveau.cs b/DEM.Net.Core/Services/CourbesNiveau/BeanParametresCalculCourbesNiveau.cs new file mode 100644 index 00000000..e55ff310 --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/BeanParametresCalculCourbesNiveau.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public class BeanParametresCalculCourbesNiveau + { + public enumModeCalculCourbe p00_modeCalculCourbes { get; set; } + // + public enumModeDeduplicationPoints p11_modaliteDeDeduplicationGeomDesPoints { get; set; } + + /// + /// Attention: le setteur doit modifier le nbre décimales d'arrondi DuCodeVertex + /// + public double p12_pasDeDepublicationDesPointsEnM { get; internal set; } + + public enumModeAgregationDesPoints p13_modeAgregationDesPoints { get; set; } + + /// + /// ATTENTION: il faut adapter cette valeur au pas de duplication: le niveau de précision doit être supérieur au pas de duplication + /// + public int p14_nbreDecimalesDArrondiPourCodeVertex { get; internal set; } + // + public double p21_ecartEntreCourbes { get; set; } + public double p22_valeurDeLaCourbe0 { get; set; } + } +} diff --git a/DEM.Net.Core/Services/CourbesNiveau/BeanPointDecoup_internal.cs b/DEM.Net.Core/Services/CourbesNiveau/BeanPointDecoup_internal.cs new file mode 100644 index 00000000..f68f003a --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/BeanPointDecoup_internal.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public class BeanPointDecoup_internal + { + public string p00_hcodePoint { get; set; } + + // + public double p01_coordX { get; set; } + public double p02_coordY { get; set; } + public int p03_Srid { get; set; } + + } +} diff --git a/DEM.Net.Core/Services/CourbesNiveau/CourbesNiveauServices.cs b/DEM.Net.Core/Services/CourbesNiveau/CourbesNiveauServices.cs new file mode 100644 index 00000000..5a9dca75 --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/CourbesNiveauServices.cs @@ -0,0 +1,991 @@ +using DEM.Net.Core.Services.Lab; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public class CourbesNiveauServices : ICourbesNiveauServices + { + #region parametres + public BeanParametresCalculCourbesNiveau GetParametresCalculCourbesNiveauParDefaut(double p_valeurCourbe0, double p_ecartEntreCourbes, double p_pasDeDeduplicationEnM = 10) + { + BeanParametresCalculCourbesNiveau v_paramOut = new BeanParametresCalculCourbesNiveau(); + try + { + v_paramOut.p00_modeCalculCourbes = enumModeCalculCourbe.interpolationLineaireSurTriangulation; + // + v_paramOut.p11_modaliteDeDeduplicationGeomDesPoints = enumModeDeduplicationPoints.manhattanParArrondi; + v_paramOut.p12_pasDeDepublicationDesPointsEnM = p_pasDeDeduplicationEnM; + v_paramOut.p13_modeAgregationDesPoints = enumModeAgregationDesPoints.valeurMax; + // + v_paramOut.p21_ecartEntreCourbes = p_ecartEntreCourbes; + v_paramOut.p22_valeurDeLaCourbe0 = 0; + GetParametresCalculCourbesNiveau_majPasDeDuplicationByRef(ref v_paramOut, p_pasDeDeduplicationEnM); + } + catch (Exception) + { + + throw; + } + return v_paramOut; + } + public void GetParametresCalculCourbesNiveau_majPasDeDuplicationByRef(ref BeanParametresCalculCourbesNiveau p_parametresAModifier, double p_pasDeDeduplicationEnM) + { + try + { + int v_nbreDecimalesDeDed=(p_pasDeDeduplicationEnM-Math.Floor(p_pasDeDeduplicationEnM)).ToString().Length-2; + int v_nbreDecimalesPourArrondi = v_nbreDecimalesDeDed + 1; + p_parametresAModifier.p12_pasDeDepublicationDesPointsEnM = p_pasDeDeduplicationEnM; + p_parametresAModifier.p14_nbreDecimalesDArrondiPourCodeVertex = v_nbreDecimalesPourArrondi; + } + catch (Exception) + { + + throw; + } + } + #endregion parametres + + + /// + /// Méthode de calcul des courbes des niveaux par interpolation linéaire sur une couche irrégulière de points + /// (Fonctionne bien sur sur une couche régulière mais inutilement compliqué pour cela!) + /// Principe: on effectue une triangulation et un pavage triangulaire de Delaunay. + /// On élimine les triangles 'plats' + /// On fait, pour chaque arc de chaque triangle 'non plat' une interpolation linéaire pour identifier les points de passage des courbes + /// =>On créé les arcs correspondants + /// + /// + /// + public BeanCourbes GetCourbesNiveau_interpolationLineaireSurTriangulation(BeanTopologieFacettes p_triangulation, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + BeanCourbes v_beanResultat = new BeanCourbes(); + try + { + // + Dictionary v_trianglesAvecData = p_triangulation.p13_facettesById; + Dictionary p_points = p_triangulation.p11_pointsFacettesByIdPoint; + v_beanResultat.p01_parametresCalculDesCourbes = p_parametresCalculDesCourbes; + + + + //ON POSE 3 DICOS: + Dictionary> v_fractionsDeCourbesParCodeCourbe = new Dictionary>(); + Dictionary> v_trianglesPlatsParCodeCourbe = new Dictionary>(); + List v_arcsPeripherieToClosePolygones = new List(); + + //=>Le premier dico contient les fractions de courbes issues des triangles. + // La clé du dico principal est le code de la courbe. + // Le dico inclus contient les fractions de courbes désignées par leur code fraction. + // ? ce dico plutôt qu'une liste=>2 triangles connexes partagent un même arc; si cet arc constitue une fraction de courbe, il ne faut pas l'insérer 2 fois + + //=>Le second dico contient les 'triangles plats' (= triangles dont les valeurs associées aux sommets sont égales) tels que la valeur associée est celle d'une courbe de niveau: + //On les stocke (pour debug) MAIS on ne va... rien à en faire! + //??=>Ils décrivent des zones 'plates': + //pour un même niveau, ces triangles 'plats' seraient fusionnés et seule la bordure nous interesserait. + //Si on excepte la périphérie la plus extérieure de l'espace, ces 'bordures' sont aussi celles d'un triangle 'non plat' + //=>Elles seront donc extraites de ces triangles 'non plats' + // (La clé est le code de la courbe, la valeur: l'id de l'îlot triangle) + + //=>Le 3ème contient la géométrie des arcs périphériques. Il doit aider à la fermeture des polygones + + + //On détermine une VALEUR de REJET: + //? une valeur purement technique permettant, dans des méthodes retournant un double, + //d'indiquer que la valeur retournée n'est pas un résultat utilisable du calcul + double v_valeurRejet = GetValeurRejet(p_points.Values.ToList(), p_parametresCalculDesCourbes, -99999); + + + foreach (KeyValuePair v_triangle in v_trianglesAvecData.OrderBy(c=>c.Key)) + { + TraitementDuTriangleCourantByRef(v_triangle.Value, ref v_trianglesPlatsParCodeCourbe, ref v_fractionsDeCourbesParCodeCourbe, ref v_arcsPeripherieToClosePolygones,p_parametresCalculDesCourbes, v_valeurRejet); + } + + + + //On RECHERCHE les éventuels 'EXTREMUMS LOCAUX' (points 'sommets' et points 'fonds de cuvette') + //=>vont constituer des courbes particulières se réduisant à...un point + Dictionary> v_extremumsLocaux; + v_extremumsLocaux=GetPointsExtremumsLocaux(p_triangulation, p_points, p_parametresCalculDesCourbes); + //On les ajoute: + if (v_extremumsLocaux != null && v_extremumsLocaux.Count>0) + { + //On souhaite garder uniquement ceux sur une courbe (ou plutôt sur un "niveau de courbe") + Dictionary> v_extremumsLocauxFiltres = new Dictionary>(); + foreach (KeyValuePair> v_courbe in v_extremumsLocauxFiltres) + { + if (GetIntervalleDAppartenanceDuPoint(Convert.ToInt16(v_courbe.Key), p_parametresCalculDesCourbes).Count() == 1) + { + v_extremumsLocauxFiltres.Add(v_courbe.Key, v_courbe.Value); + } + } + foreach (KeyValuePair< string, Dictionary < string, BeanFractionCourbe >> v_courbe in v_extremumsLocauxFiltres) + { + if (!v_fractionsDeCourbesParCodeCourbe.ContainsKey(v_courbe.Key)) + { + v_fractionsDeCourbesParCodeCourbe.Add(v_courbe.Key, new Dictionary()); + } + foreach(KeyValuePair v_point in v_courbe.Value) + { + v_fractionsDeCourbesParCodeCourbe[v_courbe.Key].Add(v_point.Key, v_point.Value); + } + } + } + + //Indexation des classes + int v_indexClasse= 0; + v_beanResultat.p10_labelClassesParIndexClasse=v_fractionsDeCourbesParCodeCourbe.Select(c => Convert.ToDouble(c.Key)).OrderBy(c => c).ToDictionary(c => v_indexClasse++, c => c.ToString()); + v_beanResultat.p11_indexClasseParLabelClasse = v_beanResultat.p10_labelClassesParIndexClasse.ToDictionary(c => c.Value, c => c.Key); + + //ASSEMBLAGE des SEGMENTS de COURBE + Dictionary>> v_courbesCoordParNiveau; + v_courbesCoordParNiveau=AssemblageDesFractionsDeCourbes(v_fractionsDeCourbesParCodeCourbe); + + //=>Application sur le bean + v_beanResultat.p12_tousSegmentsCourbesParNiveau = v_fractionsDeCourbesParCodeCourbe; + v_beanResultat.p13_arcsPeripherieToClosePolygones= v_arcsPeripherieToClosePolygones; + v_beanResultat.p14_courbesAssembleesCoordParNiveau = v_courbesCoordParNiveau; + + } + catch (Exception) + { + + throw; + } + return v_beanResultat; + } + + internal void TraitementDuTriangleCourantByRef(BeanFacette_internal p_triangleCourant, ref Dictionary> p_dicoGlobalDesTrianglesPourFusion, ref Dictionary> p_dicoGlobalDesfractionsDeCourbesParCodeCourbe,ref List p_fractionsContourExterieur, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes, double p_valeurRejet = -999999) + { + try + { + int v_srid = p_triangleCourant.p01_pointsDeFacette.First().p11_srid; + + double v_valeurCourbeTrianglePlat; + //1-Si le triangle est 'plat'(=ses 3 sommets sont associés à une même valeur) + if (IsTrianglePlat(p_triangleCourant)) + { + //=>On détermine si ces sommets sont strictement sur une courbe de niveau + //Si c'est le cas, on stocke la référence de l'îlot avec la valeur de la courbe associée: + // [=>on l'agrégera, plus tard, si besoin avec d'autres îlots voisins qui seraient dans le même cas et sur la même courbe; + // sinon, ses arcs devront être utilisées comme fractions de courbe de niveau ] + //Dans le cas contraire, les valeurs sont forcément entre 2 courbes=>on peut ignorer l'îlot + v_valeurCourbeTrianglePlat = GetValeurCourbeSiTrianglePlatUtileValeurParDefautSinon(p_triangleCourant, p_parametresCalculDesCourbes, p_valeurRejet); + if (v_valeurCourbeTrianglePlat != p_valeurRejet) + { + if (!p_dicoGlobalDesTrianglesPourFusion.ContainsKey(v_valeurCourbeTrianglePlat.ToString())) + { + p_dicoGlobalDesTrianglesPourFusion.Add(v_valeurCourbeTrianglePlat.ToString(), new List()); + } + p_dicoGlobalDesTrianglesPourFusion[v_valeurCourbeTrianglePlat.ToString()].Add(p_triangleCourant.p00_idFacette); + } + //Récup des éventuels arcs 'périphériques' + p_fractionsContourExterieur.AddRange(p_triangleCourant.p02_arcs.Where(c => c.p20_statutArc == enumStatutArc.arcExterne).ToList()); + + return; + } + + //________________________________ + //2-Si le triangle n'est pas 'plat': + //=>On est potentiellement amené à le 'découper' + + Dictionary> v_pointsDeDecoupDuTriangleByCodeCourbe = new Dictionary>(); + //(La clé du dico principal:=>le code de la courbe + //(La clé du dico secondaire:=>le code du point + + //2a-Le triangle a t-il des sommets strictement sur des courbes de niveau ? + double[] v_courbeAuDessousEtAuDessus; + string v_codeCourbe; + foreach (BeanPoint_internal v_sommet in p_triangleCourant.p01_pointsDeFacette) + { + v_courbeAuDessousEtAuDessus=GetIntervalleDAppartenanceDuPoint(v_sommet.p10_coord[2], p_parametresCalculDesCourbes); + //Si une seule valeur, alors le point est sur la courbe + if (v_courbeAuDessousEtAuDessus.Count()==1) + { + v_codeCourbe = v_courbeAuDessousEtAuDessus[0].ToString(); + PutPointDecoupeInPointDecoupeDuTriangleByRef(v_codeCourbe, v_sommet.p10_coord[0], v_sommet.p10_coord[1],v_srid, ref v_pointsDeDecoupDuTriangleByCodeCourbe, p_parametresCalculDesCourbes); + } + } + + + //2b-On chercle les éventuels points de découpe des arcs du triangle + Dictionary> v_pointsDecoupeDeLArc; + foreach (BeanArc_internal v_arcDelaunay in p_triangleCourant.p02_arcs) + { + //(La situation nominale est, 'au plus, 1 un seul point par arc pour une courbe donnée' + //SAUF si les 2 points extrêmes sont à la même 'altitude' et appartiennent à une courbe' + // Dans ce cas, tout le segment doit être conservé'. + v_pointsDecoupeDeLArc = GetPointsDecoupesByCouplePointsAlti(v_arcDelaunay.p11_pointDbt, v_arcDelaunay.p12_pointFin, p_parametresCalculDesCourbes); + foreach(KeyValuePair> v_pointsSurCourbePourLArc in v_pointsDecoupeDeLArc) + { + foreach(BeanPointDecoup_internal v_point in v_pointsSurCourbePourLArc.Value) + { + PutPointDecoupeInPointDecoupeDuTriangleByRef(v_pointsSurCourbePourLArc.Key, v_point.p01_coordX, v_point.p02_coordY, v_srid, ref v_pointsDeDecoupDuTriangleByCodeCourbe, p_parametresCalculDesCourbes); + } + } + + //On effectue, dans la même passe, un traitement spécifique sur les arcs externes: + //?Les 'arcs extérieurs' vont être utiles pour constituer les polygones entre 2 courbes. + //[Note: attention! Du coup, il ne constitue pas des 'fractions' de courbe (par définition, d'altitude homogène) + //mais bien une liaison entre 2 courbes + if (v_arcDelaunay.p20_statutArc==enumStatutArc.arcExterne) + { + ExtraitLesIntercourbesSurUnArcExterneByRef(v_arcDelaunay, v_pointsDecoupeDeLArc, ref p_fractionsContourExterieur); + + } + } + + + //On déduit les fractions d'arcs s'il y a lieu + if (v_pointsDeDecoupDuTriangleByCodeCourbe!=null && v_pointsDeDecoupDuTriangleByCodeCourbe.Count>0) + { + Dictionary v_fractionsDeCourbesDuTriangleByCodeCourbe; + v_fractionsDeCourbesDuTriangleByCodeCourbe = GetFractionsCourbesByPointsDecoupeTriangle(v_pointsDeDecoupDuTriangleByCodeCourbe); + + //On les injecte dans le dico global + foreach (KeyValuePair v_fraction in v_fractionsDeCourbesDuTriangleByCodeCourbe) + { + if (!p_dicoGlobalDesfractionsDeCourbesParCodeCourbe.ContainsKey(v_fraction.Key)) + { + p_dicoGlobalDesfractionsDeCourbesParCodeCourbe.Add(v_fraction.Key, new Dictionary()); + } + if (!p_dicoGlobalDesfractionsDeCourbesParCodeCourbe[v_fraction.Key].ContainsKey(v_fraction.Value.p00_codeFractionCourbe)) + { + p_dicoGlobalDesfractionsDeCourbesParCodeCourbe[v_fraction.Key].Add(v_fraction.Value.p00_codeFractionCourbe, v_fraction.Value); + } + } + } + } + catch (Exception) + { + + throw; + } + } + /// + ///On effectue un traitement spécifique sur les arcs externes: + ///?Les 'arcs extérieurs' vont être utiles pour constituer les polygones entre 2 courbes. + ///[Note: attention! Du coup, il ne constitue pas des 'fractions' de courbe (par définition, d'altitude homogène) + ///mais bien une liaison entre 2 courbes + /// + /// + /// + /// + internal void ExtraitLesIntercourbesSurUnArcExterneByRef(BeanArc_internal p_arcDelaunay, Dictionary> p_pointsDecoupeDeLArc, ref List p_fractionsContourExterieur) + { + try + { + if (p_arcDelaunay.p20_statutArc != enumStatutArc.arcExterne) + { + return; + } + //Si l'arc extérieur n'est pas associé à un ou des points de découpe, alors on le conserve intégralement: + if (p_pointsDecoupeDeLArc == null || p_pointsDecoupeDeLArc.Count == 0) + { + p_fractionsContourExterieur.Add(p_arcDelaunay); + } + //Sinon, on calcule les fractions d'arcs 'entre 2 courbes' + else + { + //On ordonne les points de découpe par distance au premier point de l'arc + List v_points = new List(); + foreach (List v_listePointsDec in p_pointsDecoupeDeLArc.Values) + { + v_points.AddRange(v_listePointsDec); + } + List v_pointsOrdonnances = new List(); + + double[] v_coordDbt = p_arcDelaunay.p11_pointDbt.p10_coord; + double[] v_coordFin = p_arcDelaunay.p12_pointFin.p10_coord; + + v_pointsOrdonnances.Add(v_coordDbt); + v_pointsOrdonnances.AddRange(v_points + .OrderBy(c => ((c.p01_coordX - v_coordDbt[0]) * (c.p01_coordX - v_coordDbt[0])) + ((c.p02_coordY - v_coordDbt[1]) * (c.p02_coordY - v_coordDbt[1]))) + .Select(c => new double[2] { c.p01_coordX, c.p02_coordY }) + .ToList()); + v_pointsOrdonnances.Add(v_coordFin); + + + //On calcule les fractions d'arc inter-courbe: + int v_srid = p_arcDelaunay.p11_pointDbt.p11_srid; + BeanArc_internal v_arc; + BeanPoint_internal v_pointDbt; + BeanPoint_internal v_pointFin; + for (int v_ind = 0; v_ind < v_pointsOrdonnances.Count - 1; v_ind++) + { + if (v_pointsOrdonnances[v_ind][0] == v_pointsOrdonnances[v_ind + 1][0] && v_pointsOrdonnances[v_ind][1] == v_pointsOrdonnances[v_ind + 1][1]) + { + continue; + } + v_pointDbt = new BeanPoint_internal(v_pointsOrdonnances[v_ind][0], v_pointsOrdonnances[v_ind][1], 0, v_srid); + v_pointFin = new BeanPoint_internal(v_pointsOrdonnances[v_ind + 1][0], v_pointsOrdonnances[v_ind + 1][1], 0, v_srid); + v_arc = new BeanArc_internal(v_pointDbt, v_pointFin); + + p_fractionsContourExterieur.Add(v_arc); + } + } + + } + catch (Exception) + { + + throw; + } + } + internal Dictionary GetFractionsCourbesByPointsDecoupeTriangle(Dictionary> p_pointsDecoupeDuTriangle) + { + Dictionary v_fractionsDeCourbes = new Dictionary(); + try + { + // + BeanFractionCourbe v_fractionCourbe; + foreach(KeyValuePair> v_codeCourbe in p_pointsDecoupeDuTriangle) + { + v_fractionCourbe = new BeanFractionCourbe(); + v_fractionCourbe.p01_point_1 = v_codeCourbe.Value.First().Value; + + //=>Les points isolés peuvent avoir du sens (1 'pic' ou 1 'fond de cuvette') mais il semble plus pertinent de les identifier à part. + if (v_codeCourbe.Value.Count==1) + { + continue; + } + + //=>Mais, dans le cas général, on va produire des lignes: + v_fractionCourbe.p02_point_2 = v_codeCourbe.Value.Last().Value; + v_fractionCourbe.p11_estLigneSinonPoint_vf = true; + //v_fractionCourbe.p10_geom = v_topologieServices.GetLineStringByCoord(v_codeCourbe.Value.First().Value.p01_coordX, v_codeCourbe.Value.First().Value.p02_coordY, v_codeCourbe.Value.Last().Value.p01_coordX, v_codeCourbe.Value.Last().Value.p02_coordY); + v_fractionCourbe.p20_valeurCourbe = v_codeCourbe.Key; + + //Je veux un hcode dépendant de la géométrie mais pas du sens de cette géom + if (v_codeCourbe.Value.First().Value.p01_coordX== v_codeCourbe.Value.Last().Value.p01_coordX) + { + if (v_codeCourbe.Value.First().Value.p02_coordY < v_codeCourbe.Value.Last().Value.p02_coordY) + { + v_fractionCourbe.p00_codeFractionCourbe = v_codeCourbe.Value.First().Value.p00_hcodePoint + "_" + v_codeCourbe.Value.Last().Value.p00_hcodePoint; + } + else + { + v_fractionCourbe.p00_codeFractionCourbe = v_codeCourbe.Value.Last().Value.p00_hcodePoint + "_" + v_codeCourbe.Value.First().Value.p00_hcodePoint ; + } + } + else + { + if (v_codeCourbe.Value.First().Value.p01_coordX < v_codeCourbe.Value.Last().Value.p01_coordX) + { + v_fractionCourbe.p00_codeFractionCourbe = v_codeCourbe.Value.First().Value.p00_hcodePoint + "_" + v_codeCourbe.Value.Last().Value.p00_hcodePoint; + } + else + { + v_fractionCourbe.p00_codeFractionCourbe = v_codeCourbe.Value.Last().Value.p00_hcodePoint + "_" + v_codeCourbe.Value.First().Value.p00_hcodePoint; + } + } + // + v_fractionsDeCourbes.Add(v_codeCourbe.Key, v_fractionCourbe); + } + } + catch (Exception) + { + + throw; + } + return v_fractionsDeCourbes; + } + internal Dictionary> GetPointsDecoupesByCouplePointsAlti(BeanPoint_internal p_pointAlti_1, BeanPoint_internal p_pointAlti_2, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + Dictionary> v_pointsDeDecoupeDeLArc = new Dictionary>(); + try + { + BeanPoint_internal v_pointBas; + BeanPoint_internal v_pointHaut; + BeanPointDecoup_internal v_pointDecoupToInsert; + string v_codeCourbe; + int v_srid = p_pointAlti_1.p11_srid; + //On récupère et on ordonne les points par altitude + if (p_pointAlti_1.p10_coord[2] <= p_pointAlti_2.p10_coord[2]) + { + v_pointBas = p_pointAlti_1; + v_pointHaut = p_pointAlti_2; + } + else + { + v_pointBas = p_pointAlti_2; + v_pointHaut = p_pointAlti_1; + } + // + double[] v_seuilsDuPointBas=GetIntervalleDAppartenanceDuPoint(v_pointBas.p10_coord[2], p_parametresCalculDesCourbes); + double[] v_seuilsDuPointHaut=GetIntervalleDAppartenanceDuPoint(v_pointHaut.p10_coord[2], p_parametresCalculDesCourbes); + + //Si l'un et l'autre point sont entre les mêmes courbes=>pas de points à créer + if (v_seuilsDuPointBas.Count()==2 && v_seuilsDuPointHaut.Count()==2 && v_seuilsDuPointBas[0]== v_seuilsDuPointHaut[0]) + { + return v_pointsDeDecoupeDeLArc; + } + + //Si l'un ou l'autre point est précisément sur une courbe, on l'injecte + if (v_seuilsDuPointBas.Count()==1) + { + v_codeCourbe = v_seuilsDuPointBas[0].ToString(); //[0 car dans ce cas, un seul seuil a du être renvoyé + v_pointDecoupToInsert = GetPointDecoupeInternal(v_pointBas.p10_coord[0], v_pointBas.p10_coord[1], v_srid, p_parametresCalculDesCourbes); + if (!v_pointsDeDecoupeDeLArc.ContainsKey(v_codeCourbe)) + { + v_pointsDeDecoupeDeLArc.Add(v_codeCourbe, new List()); + } + v_pointsDeDecoupeDeLArc[v_codeCourbe].Add(v_pointDecoupToInsert); + } + + if (v_seuilsDuPointHaut.Count() == 1) + { + v_codeCourbe = v_seuilsDuPointHaut[0].ToString(); //[0 car dans ce cas, un seul seuil a du être renvoyé + v_pointDecoupToInsert = GetPointDecoupeInternal(v_pointHaut.p10_coord[0], v_pointHaut.p10_coord[1], v_srid, p_parametresCalculDesCourbes); + if (!v_pointsDeDecoupeDeLArc.ContainsKey(v_codeCourbe)) + { + v_pointsDeDecoupeDeLArc.Add(v_codeCourbe, new List()); + } + v_pointsDeDecoupeDeLArc[v_codeCourbe].Add(v_pointDecoupToInsert); + } + + double v_amplitudeValeurs; + v_amplitudeValeurs = v_pointHaut.p10_coord[2] - v_pointBas.p10_coord[2]; + //Si les deux points ont les mêmes valeurs, on peut terminer (il peut donc y avoir entre 0 et 2 points en sortie + if (v_amplitudeValeurs==0) + { + return v_pointsDeDecoupeDeLArc; + } + + //Calcul des points intercalaires + double[] v_vecteurDelaunay = new double[2]; + v_vecteurDelaunay[0] = v_pointHaut.p10_coord[0] - v_pointBas.p10_coord[0]; + v_vecteurDelaunay[1] = v_pointHaut.p10_coord[1] - v_pointBas.p10_coord[1]; + + double[] v_vecteurDecoup = new double[2]; + double v_tx; + double v_ecartAuPointOrigine; + v_ecartAuPointOrigine = (v_seuilsDuPointBas.Max(c => c) - v_pointBas.p10_coord[2]); + + double v_valeurCourbePourDecoupe; + v_valeurCourbePourDecoupe = v_seuilsDuPointBas.Max(c => c); + if (v_ecartAuPointOrigine==0) //(Si l'écart est 0: le point de découpe a déjà été injecté + { + v_ecartAuPointOrigine += p_parametresCalculDesCourbes.p21_ecartEntreCourbes; + v_valeurCourbePourDecoupe += p_parametresCalculDesCourbes.p21_ecartEntreCourbes; + } + while (v_amplitudeValeurs > v_ecartAuPointOrigine)//Je mets bien strictement sup: le dernier point, si sur la courbe, a déjà été injecté. + { + v_tx = v_ecartAuPointOrigine / v_amplitudeValeurs; + v_vecteurDecoup[0] = (v_vecteurDelaunay[0] * v_tx)+ (double)v_pointBas.p10_coord[0]; + v_vecteurDecoup[1] = v_vecteurDelaunay[1] * v_tx + (double)v_pointBas.p10_coord[1]; + v_pointDecoupToInsert = GetPointDecoupeInternal(v_vecteurDecoup[0], v_vecteurDecoup[1], v_srid,p_parametresCalculDesCourbes); + + v_codeCourbe = v_valeurCourbePourDecoupe.ToString(); + if (!v_pointsDeDecoupeDeLArc.ContainsKey(v_codeCourbe)) + { + v_pointsDeDecoupeDeLArc.Add(v_codeCourbe, new List()); + } + v_pointsDeDecoupeDeLArc[v_codeCourbe].Add(v_pointDecoupToInsert); + // + v_ecartAuPointOrigine += p_parametresCalculDesCourbes.p21_ecartEntreCourbes; + v_valeurCourbePourDecoupe += p_parametresCalculDesCourbes.p21_ecartEntreCourbes; + } + } + catch (Exception) + { + throw; + } + return v_pointsDeDecoupeDeLArc; + } + internal Dictionary> GetPointsExtremumsLocaux(BeanTopologieFacettes p_triangulationDelaunay, Dictionary p_dicoDesPointsAltiByIdPoint, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + Dictionary> v_pointsSommets = new Dictionary>(); + try + { + List v_pointsAltiVoisins; + BeanPointDecoup_internal v_pointSommet; + BeanFractionCourbe v_courbePoint; + int v_srid = p_triangulationDelaunay.p00_pointsSources.First().p11_srid; + + foreach (BeanPoint_internal v_pointAlti in p_dicoDesPointsAltiByIdPoint.Values) + { + v_pointsAltiVoisins = new List(); + foreach(BeanArc_internal v_arc in v_pointAlti.p41_arcsAssocies.Values) + { + if(v_arc.p11_pointDbt.p00_id== v_pointAlti.p00_id) + { + v_pointsAltiVoisins.Add(v_arc.p12_pointFin); + } + else + { + v_pointsAltiVoisins.Add(v_arc.p11_pointDbt); + } + } + + //Si la valeur est strictement plus petite ou plus grande que celle de ses voisins=>c'est un extremum local. + if (v_pointsAltiVoisins.Min(c => c.p10_coord[2]) > v_pointAlti.p10_coord[2] || v_pointsAltiVoisins.Max(c => c.p10_coord[2]) < v_pointAlti.p10_coord[2]) + { + v_pointSommet = GetPointDecoupeInternal(v_pointAlti.p10_coord[0], v_pointAlti.p10_coord[1], v_srid, p_parametresCalculDesCourbes); + //On créé une pseudo-courbe (va être, en fait, un point!) + v_courbePoint = new BeanFractionCourbe(); + v_courbePoint.p01_point_1 = v_pointSommet; + v_courbePoint.p11_estLigneSinonPoint_vf = false; + v_courbePoint.p00_codeFractionCourbe = v_pointSommet.p00_hcodePoint; + //v_courbePoint.p10_geom + v_courbePoint.p20_valeurCourbe = v_pointAlti.p10_coord[2].ToString(); + if (!v_pointsSommets.ContainsKey(v_pointAlti.p10_coord[2].ToString())) + { + v_pointsSommets.Add(v_pointAlti.p10_coord[2].ToString(), new Dictionary()); + } + v_pointsSommets[v_pointAlti.p10_coord[2].ToString()].Add(v_courbePoint.p00_codeFractionCourbe, v_courbePoint); + } + } + } + catch (Exception) + { + + throw; + } + return v_pointsSommets; + } + internal void PutPointDecoupeInPointDecoupeDuTriangleByRef(string v_codeCourbe, double p_coordX, double p_coordY,int p_srid, ref Dictionary> p_pointsDeDecoupDuTriangleByCodeCourbe, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + try + { + if (!p_pointsDeDecoupDuTriangleByCodeCourbe.ContainsKey(v_codeCourbe)) + { + p_pointsDeDecoupDuTriangleByCodeCourbe.Add(v_codeCourbe, new Dictionary()); + } + BeanPointDecoup_internal v_pointDecoupe = GetPointDecoupeInternal(p_coordX, p_coordY, p_srid, p_parametresCalculDesCourbes); + if (!p_pointsDeDecoupDuTriangleByCodeCourbe[v_codeCourbe].ContainsKey(v_pointDecoupe.p00_hcodePoint)) + { + p_pointsDeDecoupDuTriangleByCodeCourbe[v_codeCourbe].Add(v_pointDecoupe.p00_hcodePoint, v_pointDecoupe); + } + } + catch (Exception) + { + + throw; + } + } + internal BeanPointDecoup_internal GetPointDecoupeInternal(double p_coordX, double p_coordY,int p_srid, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + BeanPointDecoup_internal v_pointDecoup = new BeanPointDecoup_internal(); + try + { + v_pointDecoup.p01_coordX = p_coordX; + v_pointDecoup.p02_coordY = p_coordY; + v_pointDecoup.p03_Srid = p_srid; + + //v_pointDecoup.p00_hcodePoint = GetHCodePoint(p_coordX, p_coordY, p_parametresCalculDesCourbes.p14_nbreDecimalesDArrondiPourCodeVertex); + v_pointDecoup.p00_hcodePoint = GetHCodePoint(p_coordX, p_coordY); + + + } + catch (Exception) + { + + throw; + } + return v_pointDecoup; + } + + /// + /// Sert simplement à générer une valeur qui sert à indiquer: 'résultat invalide'! + /// =>Permet d'en définir une sans risque de collision avec des valeurs utiles + /// + /// + /// + /// + /// + internal double GetValeurRejet(List p_pointsAlti, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes, double v_valeurRejetParDefaut = -999999) + { + double v_valeurRejetOut = v_valeurRejetParDefaut; + try + { + double v_valeurMin = p_pointsAlti.Min(c => c.p10_coord[2]); + double v_courbeMinAuPire = v_valeurMin - p_parametresCalculDesCourbes.p21_ecartEntreCourbes; + + double v_valeurRejet = v_valeurRejetParDefaut; + if (v_valeurRejet > v_courbeMinAuPire) + { + v_valeurRejet = v_courbeMinAuPire - 1000; + } + } + catch (Exception) + { + + throw; + } + return v_valeurRejetOut; + } + + + + #region Contrôle triangle plats + /// + ///[Il ne s'agit pas ici de la notion géométrique de 'triangle plat' (=triangle sans surface) + ///=>Ici, on considère les valeurs associées à chaque sommet du triangle: + /// Si toutes les valeurs sont égales, le triangle est déclaré 'plat'. + /// Il vaudrait meiux employer le terme de 'tétraèdre plat' ou de 'pyramide plate'! + /// + /// + /// + private bool IsTrianglePlat(BeanFacette_internal p_triangle) + { + return (p_triangle.p01_pointsDeFacette.Select(c => c.p10_coord[2]).Distinct().Count() == 1); + } + private bool IsTrianglePlatUtile(BeanFacette_internal p_trianglePlat, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes) + { + try + { + if (!IsTrianglePlat(p_trianglePlat)) //On contrôle qd même qu'il est plat! + { + throw new Exception("Le triangle demandé n'est pas plat."); + } + double v_valeurPoints = p_trianglePlat.p01_pointsDeFacette.Select(c => c.p10_coord[2]).Distinct().First(); + double[] v_courbesEnDessousEtEnDessus; + v_courbesEnDessousEtEnDessus = GetIntervalleDAppartenanceDuPoint(v_valeurPoints, p_parametresCalculDesCourbes); + //=>Si une seule valeur=>le point est strictement sur la courbe + if (v_courbesEnDessousEtEnDessus.Count()==1) + { + return true; + } + else + { + return false; + } + } + catch (Exception) + { + + throw; + } + } + private double GetValeurCourbeSiTrianglePlatUtileValeurParDefautSinon(BeanFacette_internal p_triangle, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes, double p_valeurParDefautSiNonConforme= -99999) + { + try + { + //1-Est-il 'plat' ? + if (!IsTrianglePlat(p_triangle)) + { + return p_valeurParDefautSiNonConforme; + } + //2- + double v_valeurPoints = p_triangle.p01_pointsDeFacette.Select(c => c.p10_coord[2]).Distinct().First(); + double[] v_courbesEnDessousEtEnDessus; + v_courbesEnDessousEtEnDessus = GetIntervalleDAppartenanceDuPoint(v_valeurPoints, p_parametresCalculDesCourbes); + //=>Si une seule valeur=>le point est strictement sur la courbe + if (v_courbesEnDessousEtEnDessus.Count() == 1) + { + return v_courbesEnDessousEtEnDessus.First(); + } + else + { + return p_valeurParDefautSiNonConforme; + } + } + catch (Exception) + { + + throw; + } + } + #endregion Contrôle triangle plats + + internal string GetHCodePoint(double p_coordX, double p_coordY) + { + string v_hashCode; + try + { + double[] v_coord = new double[2] { p_coordX, p_coordY }; + v_hashCode = FLabServices.createUtilitaires().GetHCodeGeogPoint(v_coord); + } + catch (Exception) + { + throw; + } + return v_hashCode; + } + //internal string GetHCodePoint(double p_coordX, double p_coordY, int p_nbreDecimalesPourArrondi, char p_separateur='_') + //{ + // string v_hashCode; + // try + // { + // v_hashCode = Math.Round(p_coordX, p_nbreDecimalesPourArrondi).ToString().Replace(",", ".") + p_separateur + Math.Round(p_coordY, p_nbreDecimalesPourArrondi).ToString().Replace(",", "."); + // } + // catch (Exception) + // { + // throw; + // } + // return v_hashCode; + //} + + #region seuil d'appartenance + /// + /// Renvoie l'"intervalle d'appartenance de la valeur": elle peut être comprise entre 2 valeurs (=2 'courbes') + /// OU être strictement sur la courbe=>Dans ce cas, 1 seule valeur est renvoyée. + /// + /// + /// + /// + private double[] GetIntervalleDAppartenanceDuPoint(double p_valeurAClasser, BeanParametresCalculCourbesNiveau p_parametresCalculCourbes) + { + double[] p_classeDAppartenanceDuPoint = null; + try + { + double v_classe; + v_classe=(p_valeurAClasser - p_parametresCalculCourbes.p22_valeurDeLaCourbe0) / p_parametresCalculCourbes.p21_ecartEntreCourbes ; + int v_seuilInf = (int)Math.Floor(v_classe); + int v_seuilSup = (int)Math.Ceiling(v_classe); + // + + if (v_seuilInf != v_seuilSup) + { + p_classeDAppartenanceDuPoint = new double[2]; + p_classeDAppartenanceDuPoint[1] = (p_parametresCalculCourbes.p21_ecartEntreCourbes * v_seuilSup) + p_parametresCalculCourbes.p22_valeurDeLaCourbe0; + } + else + { + p_classeDAppartenanceDuPoint = new double[1]; + } + p_classeDAppartenanceDuPoint[0] = (p_parametresCalculCourbes.p21_ecartEntreCourbes * v_seuilInf) + p_parametresCalculCourbes.p22_valeurDeLaCourbe0; + } + catch (Exception) + { + throw; + } + return p_classeDAppartenanceDuPoint; + } + #endregion seuil d'appartenance + + internal Dictionary>> AssemblageDesFractionsDeCourbes(Dictionary> p_fractionCourbesParNiveau) + { + Dictionary>> v_seriesCoordPoints_parNiveau=new Dictionary>>(); + try + { + //On calcule les fractions continues + //[Remontée sous forme de hcodes + Dictionary>> v_groupePointsOrdonnes_parNiveau = new Dictionary>>(); + Dictionary> v_seriesPointsDuNiveau; + string v_niveauCourant; + foreach (KeyValuePair> v_niveau in p_fractionCourbesParNiveau) + { + v_niveauCourant = v_niveau.Key; + v_seriesPointsDuNiveau = GetFractionsContinuesDeLaCourbe(v_niveau.Value); + v_groupePointsOrdonnes_parNiveau.Add(v_niveauCourant, v_seriesPointsDuNiveau); + } + + //On récupère les coordonnées: + + + Dictionary v_coordParPoint = new Dictionary(); + List v_toutesFractions= p_fractionCourbesParNiveau.Values.SelectMany(c => c.Values).ToList(); + foreach(BeanFractionCourbe v_fr in v_toutesFractions) + { + if(!v_coordParPoint.ContainsKey(v_fr.p01_point_1.p00_hcodePoint)) + { + v_coordParPoint.Add(v_fr.p01_point_1.p00_hcodePoint, new double[2] { v_fr.p01_point_1.p01_coordX, v_fr.p01_point_1.p02_coordY }); + } + if (!v_coordParPoint.ContainsKey(v_fr.p02_point_2.p00_hcodePoint)) + { + v_coordParPoint.Add(v_fr.p02_point_2.p00_hcodePoint, new double[2] { v_fr.p02_point_2.p01_coordX, v_fr.p02_point_2.p02_coordY }); + } + } + v_seriesCoordPoints_parNiveau = v_groupePointsOrdonnes_parNiveau.ToDictionary(c => c.Key, c => new Dictionary> ()); + foreach(KeyValuePair>> v_niveau in v_groupePointsOrdonnes_parNiveau) + { + foreach(KeyValuePair> v_courbe in v_niveau.Value) + { + v_seriesCoordPoints_parNiveau[v_niveau.Key].Add(v_courbe.Key, new List()); + foreach(string v_hcpt in v_courbe.Value) + { + v_seriesCoordPoints_parNiveau[v_niveau.Key][v_courbe.Key].Add(v_coordParPoint[v_hcpt]); + } + } + } + } + catch (Exception) + { + throw; + } + return v_seriesCoordPoints_parNiveau; + } + + internal Dictionary> GetFractionsContinuesDeLaCourbe(Dictionary p_fractionsDeMemeNiveau) + { + Dictionary> v_pointsOrdonnesParGroupe = new Dictionary>(); + try + { + //On référence les points de chaque arc + Dictionary> v_pointsExtremesParArc; + v_pointsExtremesParArc = p_fractionsDeMemeNiveau.ToDictionary(c => c.Key, c => new List() { c.Value.p01_point_1.p00_hcodePoint, c.Value.p02_point_2.p00_hcodePoint }); + + //et les arcs disponibles pour chaque point (cette liste se réduit au fil du traitement) + Dictionary> v_arcsDisponiblesParHCodePoint; + List v_tousHCodePoint= v_pointsExtremesParArc.Values.SelectMany(c => c).Distinct().OrderBy(c => c).ToList(); + v_arcsDisponiblesParHCodePoint = v_tousHCodePoint.Distinct().OrderBy(c => c).ToDictionary(c => c, c => new HashSet()); + foreach(KeyValuePair v_fr in p_fractionsDeMemeNiveau) + { + v_arcsDisponiblesParHCodePoint[v_fr.Value.p01_point_1.p00_hcodePoint].Add(v_fr.Key); + v_arcsDisponiblesParHCodePoint[v_fr.Value.p02_point_2.p00_hcodePoint].Add(v_fr.Key); + } + + //On initialise le suivi des arcs + Dictionary v_etatAvancementTraitementDesArcs; + v_etatAvancementTraitementDesArcs = p_fractionsDeMemeNiveau.ToDictionary(c => c.Key, c => false); + + //TRAITEMENT: + //Prncipe: + //On choisit un point de départ: par priorité un noeud pendant s'il en existe sinon un noeud dense (plus de 2 arcs) s'il y en a + //sinon n'importe quel point ayant encore un arc disponible. + //On cherche tous les points connectés à ce point originel jusqu'à : + //soit qu'on revienne au point de départ + //soit qu'il n'y ait plus d'arc. + //Dans ce cas, on teste s'il existe encore des candidats points de départ (la liste de leurs arcs dispo est mise à jour au fil des traitements) + //et on poursuit... + + + string v_hCpointDepart = GetUnPointDeDepart(v_arcsDisponiblesParHCodePoint); + int v_idGroupe = 1; + while (v_hCpointDepart!=null) + { + + if(v_hCpointDepart==null) + { + break; + } + // + List v_pointsOrdonneesDuGroupe; + v_pointsOrdonneesDuGroupe = GetHCPointsOrdonnesDuGroupe(v_hCpointDepart, ref v_arcsDisponiblesParHCodePoint, ref v_etatAvancementTraitementDesArcs, v_pointsExtremesParArc, p_fractionsDeMemeNiveau); + // + v_pointsOrdonnesParGroupe.Add(v_idGroupe, v_pointsOrdonneesDuGroupe); + // + v_hCpointDepart = GetUnPointDeDepart(v_arcsDisponiblesParHCodePoint); + v_idGroupe++; + } + } + catch (Exception) + { + + throw; + } + return v_pointsOrdonnesParGroupe; + } + private List GetHCPointsOrdonnesDuGroupe(string p_hCpointDepart, ref Dictionary> p_arcsParHCodePoint, ref Dictionary p_etatAvancementTraitementDesArcs, Dictionary> p_pointsExtremesParArc, Dictionary p_fractionsDeMemeNiveau) + { + List v_listeOrdonneesDesPointsDuGroupe = new List(); + try + { + string v_hCpointCourant= p_hCpointDepart; + v_listeOrdonneesDesPointsDuGroupe.Add(p_hCpointDepart); + while (true) + { + string v_hCodeArcDispo = GetUnArcDisponiblePourLePoint(v_hCpointCourant, p_arcsParHCodePoint, p_etatAvancementTraitementDesArcs); + if (v_hCodeArcDispo == null) + { + break; + } + string v_hCpointSuivant = GetHCodePointSuivant(v_hCpointCourant, p_fractionsDeMemeNiveau[v_hCodeArcDispo]); + //On ajoute le point à la liste + v_listeOrdonneesDesPointsDuGroupe.Add(v_hCpointSuivant); + + //On MAJ l'état d'avancement et on enlève la référence à l'arc pour les 2 points concernés: + p_etatAvancementTraitementDesArcs[v_hCodeArcDispo] = true; + p_arcsParHCodePoint[p_pointsExtremesParArc[v_hCodeArcDispo].First()].Remove(v_hCodeArcDispo); + p_arcsParHCodePoint[p_pointsExtremesParArc[v_hCodeArcDispo].Last()].Remove(v_hCodeArcDispo); + + //Si retour au point initial=>on arrête pour ce groupe (boucle/polygone) + if (v_hCpointSuivant == p_hCpointDepart) + { + break; + } + v_hCpointCourant = v_hCpointSuivant; + } + } + catch (Exception) + { + throw; + } + return v_listeOrdonneesDesPointsDuGroupe; + } + private string GetUnPointDeDepart(Dictionary> p_arcsParHCodePoint) + { + string v_hCpointDepart = null; + try + { + Dictionary> v_pointsAvecArcsUtiles; + v_pointsAvecArcsUtiles = p_arcsParHCodePoint.Where(c => c.Value.Count > 0).ToDictionary(c => c.Key, c => c.Value); + if (v_pointsAvecArcsUtiles.Count == 0) + { + return null; + } + + //On privilégie, par ordre, + //- les 'points d'extrémité' (=liés à un seul arc) + //- les 'noeuds' (liés à plus de 2 arcs) + //les autres cas (strictement 2 arcs) s'inscrivent forcément dans une boucle simple, et on peut prendre n'importe lequel + List v_pointsPrioriraires; + v_pointsPrioriraires = v_pointsAvecArcsUtiles.Where(c => c.Value.Count == 1).Select(c => c.Key).ToList(); + if (v_pointsPrioriraires.Count == 0) + { + v_pointsPrioriraires = v_pointsAvecArcsUtiles.Where(c => c.Value.Count > 2).Select(c => c.Key).ToList(); + } + if (v_pointsPrioriraires.Count == 0) + { + v_hCpointDepart = v_pointsAvecArcsUtiles.First().Key; + } + else + { + v_hCpointDepart = v_pointsPrioriraires.First(); + } + } + catch (Exception) + { + + throw; + } + return v_hCpointDepart; + } + private string GetUnArcDisponiblePourLePoint(string p_pointSource, Dictionary> p_arcsParHCodePoint, Dictionary p_etatAvancementTraitementDesArcs) + { + string v_arcOut = null; + try + { + foreach(string v_codeArc in p_arcsParHCodePoint[p_pointSource]) + { + if(!p_etatAvancementTraitementDesArcs[v_codeArc]) + { + return v_codeArc; + } + } + } + catch (Exception) + { + + throw; + } + return v_arcOut; + } + private string GetHCodePointSuivant(string p_hCodePointSource, BeanFractionCourbe p_arc) + { + string v_retour = null; + try + { + if(p_arc.p01_point_1.p00_hcodePoint== p_hCodePointSource) + { + return p_arc.p02_point_2.p00_hcodePoint; + } + if (p_arc.p02_point_2.p00_hcodePoint == p_hCodePointSource) + { + return p_arc.p01_point_1.p00_hcodePoint; + } + } + catch (Exception) + { + + throw; + } + return v_retour; + } + } +} diff --git a/DEM.Net.Core/Services/CourbesNiveau/ICourbesNiveauServices.cs b/DEM.Net.Core/Services/CourbesNiveau/ICourbesNiveauServices.cs new file mode 100644 index 00000000..6c97a2a9 --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/ICourbesNiveauServices.cs @@ -0,0 +1,38 @@ +using DEM.Net.Core.Services.Lab; +using System.Collections.Generic; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public interface ICourbesNiveauServices + { + /// + /// Calcul des courbes de niveau. + /// Une seule méthode développée à ce stade (interpol linéaire sur triangulation) mais permet de traiter les champs non réguliers. + /// Les 'points alti' contiennent des objets très simples avec un Id, une géométrie de point, une valeur numérique décrivant une "élévation" + /// (de quelque nature quelle soit: altitude, population, coût,...) + /// A noter: + /// - la méthode prévoit une déduplication préalable des points (méthode utilisée par défaut: arrondi des valeurs en x et en y). + /// - l'écart entre courbes doit être constant. + /// - cette méthode permet de générer des courbes pas des polygones. + /// - Les calculs sont prévus aujourd'hui pour des données projetées (pas sur que cela fonctionne en Lat/long) + /// + /// + /// + /// + BeanCourbes GetCourbesNiveau_interpolationLineaireSurTriangulation(BeanTopologieFacettes p_triangulation, BeanParametresCalculCourbesNiveau p_parametresCalculDesCourbes); + + /// + /// Permet de paramétrer le calcul de courbes de niveau. + /// (Notamment l'écart entre courbes, le niveau '0' (l' "altitude" par laquelle doit passer la courbe de référence à partir de laquelle on calcule les autres courbes) + /// et la distance en-deça de laquelle 2 points sont considérés comme confondus) + /// et renvoie des paramètres par défaut + /// Ces paramètres peuvent être modifiés par les setteurs sauf le pas de duplication qui doit être mis à jour par la méthode Ad hoc. + /// + /// + /// + /// + BeanParametresCalculCourbesNiveau GetParametresCalculCourbesNiveauParDefaut(double p_valeurCourbe0, double p_ecartEntreCourbes, double p_pasDeDeduplicationEnM = 10); + void GetParametresCalculCourbesNiveau_majPasDeDuplicationByRef(ref BeanParametresCalculCourbesNiveau p_parametresAModifier, double p_pasDeDeduplicationEnM); + // + } +} \ No newline at end of file diff --git a/DEM.Net.Core/Services/CourbesNiveau/enumCourbesNiveau.cs b/DEM.Net.Core/Services/CourbesNiveau/enumCourbesNiveau.cs new file mode 100644 index 00000000..ede8d0f3 --- /dev/null +++ b/DEM.Net.Core/Services/CourbesNiveau/enumCourbesNiveau.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spir.Commun.Service.Technical.Cartographie.Service.CourbesNiveau +{ + public enum enumModeCalculCourbe + { + interpolationLineaireSurTriangulation + } + + public enum enumModeDeduplicationPoints + { + manhattanParArrondi, + pasDeDeduplication + } + public enum enumPrecisionManahatan + { + precision100m, + precision10m, + precisionM, + precisionDm + } + public enum enumModeAgregationDesPoints + { + valeurMin, + valeurMax, + moyenneA + } + +} diff --git a/DEM.Net.Core/Services/Lab/BeanArc_internal.cs b/DEM.Net.Core/Services/Lab/BeanArc_internal.cs index 349cd82e..68145f26 100644 --- a/DEM.Net.Core/Services/Lab/BeanArc_internal.cs +++ b/DEM.Net.Core/Services/Lab/BeanArc_internal.cs @@ -35,11 +35,11 @@ public class BeanArc_internal { private static int _dernierIdArc = 0; // - public int p00_idArc { get; } + public int p00_idArc { get; set; } public string p01_hcodeArc { get; } // - public BeanPoint_internal p11_pointDbt { get; } - public BeanPoint_internal p12_pointFin { get; } + public BeanPoint_internal p11_pointDbt { get; set; } + public BeanPoint_internal p12_pointFin { get; set; } // public enumStatutArc p20_statutArc { get; set; } public BeanFacette_internal p21_facetteGauche { get; set; } diff --git a/DEM.Net.Core/Services/Lab/BeanPoint_internal.cs b/DEM.Net.Core/Services/Lab/BeanPoint_internal.cs index 1a9bf86b..2eea85fc 100644 --- a/DEM.Net.Core/Services/Lab/BeanPoint_internal.cs +++ b/DEM.Net.Core/Services/Lab/BeanPoint_internal.cs @@ -42,13 +42,13 @@ public string p01_hCodeGeog { set { - p01_hCodeGeogP=FLabServices.createUtilitaires().GethCodeGeogPoint(p10_coord); + p01_hCodeGeogP=FLabServices.createUtilitaires().GetHCodeGeogPoint(p10_coord); } get { if(p01_hCodeGeogP=="") { - p01_hCodeGeogP = FLabServices.createUtilitaires().GethCodeGeogPoint(p10_coord); + p01_hCodeGeogP = FLabServices.createUtilitaires().GetHCodeGeogPoint(p10_coord); } return p01_hCodeGeogP; } @@ -76,7 +76,7 @@ public BeanPoint_internal(double p_x, double p_y, double p_z, int p_srid) { p00_id = _dernierId++; p10_coord=new double[3] { p_x, p_y, p_z }; - p01_hCodeGeog = FLabServices.createUtilitaires().GethCodeGeogPoint(p10_coord); + p01_hCodeGeog = FLabServices.createUtilitaires().GetHCodeGeogPoint(p10_coord); p11_srid = p_srid; p41_arcsAssocies = new Dictionary(); p42_ordonnancementHorairesArcs = new List(); diff --git a/DEM.Net.Core/Services/Lab/BeanTopologieFacettes.cs b/DEM.Net.Core/Services/Lab/BeanTopologieFacettes.cs index 2a76a3a7..684e7428 100644 --- a/DEM.Net.Core/Services/Lab/BeanTopologieFacettes.cs +++ b/DEM.Net.Core/Services/Lab/BeanTopologieFacettes.cs @@ -38,7 +38,7 @@ public class BeanTopologieFacettes public Dictionary p11_pointsFacettesByIdPoint { get; set; } public Dictionary p12_arcsByCode { get; set; } public Dictionary p13_facettesById { get; set; } - // + // public BeanFacette_internal p21_facetteAvecEcartAbsoluMax { get; set; } diff --git a/DEM.Net.Core/Services/Lab/CalculServicesVoronoi.cs b/DEM.Net.Core/Services/Lab/CalculServicesVoronoi.cs new file mode 100644 index 00000000..5ab5f2d7 --- /dev/null +++ b/DEM.Net.Core/Services/Lab/CalculServicesVoronoi.cs @@ -0,0 +1,131 @@ +using FruitiereMapsLib.Services.Data; +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace DEM.Net.Core.Services.Lab +{ + public class CalculServicesVoronoi : ICalculServicesVoronoi + { + public BeanTopologieFacettes GetTopologieVoronoi(List p_points, int p_srid, double p_ecartMiniEnM) + { + BeanTopologieFacettes v_topol = null; + try + { + List v_edges; + v_edges = GetArcsBrutsVoronoiFromListBeanPointInternal(p_points, p_ecartMiniEnM); + // + v_topol = GetTopologieBruteFromListGraphEdges(v_edges, p_srid); + // + FServices.createCalculMedium().RecalculFacettes(ref v_topol); + } + catch (Exception) + { + + throw; + } + return v_topol; + } + + private BeanTopologieFacettes GetTopologieBruteFromListGraphEdges(List p_edges, int p_srid) + { + BeanTopologieFacettes v_topol = new BeanTopologieFacettes(); + try + { + Dictionary v_points = new Dictionary(); + Dictionary v_arcs = new Dictionary(); + // + List v_ptsDeLArc; + BeanPoint_internal v_pt_dbt; + BeanPoint_internal v_pt_fin; + BeanArc_internal v_arc; + foreach (GraphEdge v_edge in p_edges) + { + v_ptsDeLArc = GetBeanArcInternalFromGraphEdges(v_edge, p_srid); + v_pt_dbt = v_ptsDeLArc.First(); + v_pt_fin = v_ptsDeLArc.Last(); + // + if (!v_points.ContainsKey(v_pt_dbt.p01_hCodeGeog)) + { + v_points.Add(v_pt_dbt.p01_hCodeGeog, v_pt_dbt); + } + else + { + v_pt_dbt = v_points[v_pt_dbt.p01_hCodeGeog]; + } + // + if (!v_points.ContainsKey(v_pt_fin.p01_hCodeGeog)) + { + v_points.Add(v_pt_fin.p01_hCodeGeog, v_pt_fin); + } + else + { + v_pt_fin = v_points[v_pt_fin.p01_hCodeGeog]; + } + // + v_arc = new BeanArc_internal(v_pt_dbt, v_pt_fin); + // + v_arcs.Add(v_arc.p01_hcodeArc, v_arc); + + v_pt_dbt.p41_arcsAssocies.Add(v_arc.p01_hcodeArc, v_arc); + v_pt_fin.p41_arcsAssocies.Add(v_arc.p01_hcodeArc, v_arc); + } + // + v_topol.p12_arcsByCode = v_arcs; + v_topol.p00_pointsSources = v_points.Values.ToList(); + } + catch (Exception) + { + + throw; + } + return v_topol; + } + private List GetBeanArcInternalFromGraphEdges(GraphEdge p_edge, int p_srid) + { + List v_points = new List(); + try + { + BeanPoint_internal v_point1 = new BeanPoint_internal(p_edge.x1, p_edge.y1,0, p_srid); + BeanPoint_internal v_point2 = new BeanPoint_internal(p_edge.x2, p_edge.y2, 0, p_srid); + // + v_points.Add(v_point1); + v_points.Add(v_point2); + } + catch (Exception) + { + + throw; + } + return v_points; + } + private List GetArcsBrutsVoronoiFromListBeanPointInternal(List p_points, double p_ecartMiniEnM) + { + List v_edges = null; + try + { + Voronoi v_Voronoi = new Voronoi(p_ecartMiniEnM); + + double[] xVal = new double[p_points.Count]; + double[] yVal = new double[p_points.Count]; + int v_indPoint = 0; + foreach (BeanPoint_internal v_pt in p_points) + { + xVal[v_indPoint] = v_pt.p10_coord[0]; + yVal[v_indPoint] = v_pt.p10_coord[1]; + v_indPoint++; + } + double v_largeur = xVal.Max() - xVal.Min(); + double v_hauteur = yVal.Max() - yVal.Min(); + + v_edges=v_Voronoi.generateVoronoi(xVal, yVal, 0, v_largeur, 0, v_hauteur); + } + catch (Exception) + { + throw; + } + return v_edges; + } + } +} diff --git a/DEM.Net.Core/Services/Lab/CalculServices_Medium.cs b/DEM.Net.Core/Services/Lab/CalculServices_Medium.cs index 1a4a05dc..7ee7d998 100644 --- a/DEM.Net.Core/Services/Lab/CalculServices_Medium.cs +++ b/DEM.Net.Core/Services/Lab/CalculServices_Medium.cs @@ -1979,7 +1979,210 @@ public List GetOrdonnancementArcsAutourPointFacette(BeanPoint_internal p return v_codeArcsOrdonnes; } + public void RecalculFacettes(ref BeanTopologieFacettes p_topol) + { + try + { + List v_facettes = new List(); + // + BeanArc_internal v_arcInitial = p_topol.p12_arcsByCode.Values.First(); + Dictionary v_nbreBordsTraites; + v_nbreBordsTraites = p_topol.p12_arcsByCode.ToDictionary(c => c.Key, c => 0); + // + BeanFacette_internal v_facette; + BeanArc_internal v_arcATester; + int v_avct; + foreach (string v_codeArc in v_nbreBordsTraites.Keys) + { + v_avct = v_nbreBordsTraites[v_codeArc]; + if (v_avct == 2) + { + continue; + } + v_arcATester = p_topol.p12_arcsByCode[v_codeArc]; + if (v_avct == 0) + { + v_facette = ConstruitFacette(v_arcATester, ref p_topol, ref v_nbreBordsTraites, true); + v_facettes.Add(v_facette); + v_facette = ConstruitFacette(v_arcATester, ref p_topol, ref v_nbreBordsTraites, false); + v_facettes.Add(v_facette); + v_avct = 2; + } + if (v_avct == 1) + { + if(v_arcATester.p21_facetteGauche!=null) + { + v_facette = ConstruitFacette(v_arcATester, ref p_topol, ref v_nbreBordsTraites, true); + } + else + { + v_facette = ConstruitFacette(v_arcATester, ref p_topol, ref v_nbreBordsTraites, false); + } + } + } + p_topol.p13_facettesById = v_facettes.ToDictionary(c => c.p00_idFacette, c => c); + } + catch (Exception) + { + throw; + } + } + private BeanFacette_internal ConstruitFacette(BeanArc_internal p_arcATesterDebut,ref BeanTopologieFacettes p_topol, ref Dictionary p_nbreBordsTraitesParArc, bool p_aDroite_sinonAGauche) + { + BeanFacette_internal v_facette = new BeanFacette_internal(); + try + { + v_facette.p02_arcs = new List(); + v_facette.p01_pointsDeFacette = new List(); + // + if(p_aDroite_sinonAGauche) + { + p_arcATesterDebut.p22_facetteDroite = v_facette; + } + else + { + p_arcATesterDebut.p21_facetteGauche = v_facette; + } + p_nbreBordsTraitesParArc[p_arcATesterDebut.p01_hcodeArc]++; + // + v_facette.p02_arcs.Add(p_arcATesterDebut); + v_facette.p01_pointsDeFacette.Add(p_arcATesterDebut.p11_pointDbt); + // + BeanArc_internal v_arcSuivant; + v_arcSuivant = GetArcSuivant(p_arcATesterDebut, ref p_topol, p_aDroite_sinonAGauche); + while (v_arcSuivant.p01_hcodeArc!= p_arcATesterDebut.p01_hcodeArc) + { + v_facette.p02_arcs.Add(v_arcSuivant); + v_facette.p01_pointsDeFacette.Add(v_arcSuivant.p11_pointDbt); + // + if (p_aDroite_sinonAGauche) + { + v_arcSuivant.p22_facetteDroite = v_facette; + } + else + { + v_arcSuivant.p21_facetteGauche = v_facette; + } + p_nbreBordsTraitesParArc[v_arcSuivant.p01_hcodeArc]++; + } + } + catch (Exception) + { + throw; + } + return v_facette; + } + private BeanArc_internal GetArcSuivant(BeanArc_internal p_arcCourant,ref BeanTopologieFacettes p_topol, bool p_aDroite_sinonAGauche) + { + BeanArc_internal v_arcRetour = null; + try + { + BeanPoint_internal v_pointFin = p_arcCourant.p12_pointFin; + List v_arcsSuivantsOrdonnes = v_pointFin.p42_ordonnancementHorairesArcs; + + for (int v_index = 0; v_index < v_arcsSuivantsOrdonnes.Count; v_index++) + { + if (v_arcsSuivantsOrdonnes[v_index] == p_arcCourant.p01_hcodeArc) + { + if(!p_aDroite_sinonAGauche)//A gauche + { + if (v_index > 0) + { + v_arcRetour = p_topol.p12_arcsByCode[v_arcsSuivantsOrdonnes[v_index - 1]]; + } + else + { + v_arcRetour = p_topol.p12_arcsByCode[v_arcsSuivantsOrdonnes.Last()]; + } + } + ///// + /// if(p_aDroite_sinonAGauche) //A droite + { + if (v_index < v_arcsSuivantsOrdonnes.Count - 1) + { + v_arcRetour = p_topol.p12_arcsByCode[v_arcsSuivantsOrdonnes[v_index + 1]]; + } + else + { + v_arcRetour = p_topol.p12_arcsByCode[v_arcsSuivantsOrdonnes.First()]; + } + } + break; + } + }//FIN FOR + ReorienteArcSiBesoin(ref v_arcRetour, ref p_topol, v_pointFin); + } + catch (Exception) + { + throw; + } + return v_arcRetour; + } + public bool ReorienteArcSiBesoin(ref BeanArc_internal p_arc, ref BeanTopologieFacettes p_topol,BeanPoint_internal p_ptDebut) + { + if (p_arc.p12_pointFin == p_ptDebut) + { + InverserArc(ref p_arc, ref p_topol); + return true; + } + if (p_arc.p11_pointDbt== p_ptDebut) + { + return false; + } + throw new Exception("Le point " + p_ptDebut.p00_id + " n'est pas un point de l'arc " + p_arc.p00_idArc); + } + public void InverserArc(ref BeanArc_internal p_arc, ref BeanTopologieFacettes p_topol) + { + BeanPoint_internal p_ptDebut = p_arc.p11_pointDbt; + BeanPoint_internal p_ptFin = p_arc.p12_pointFin; + BeanFacette_internal p_facG = p_arc.p21_facetteGauche; + BeanFacette_internal p_facD = p_arc.p22_facetteDroite; + // + p_arc.p12_pointFin = p_ptDebut; + p_arc.p11_pointDbt = p_ptFin; + p_arc.p21_facetteGauche = p_facD; + p_arc.p22_facetteDroite = p_facG; + } + public bool SupprimerUneFacette(ref BeanTopologieFacettes p_topologieFacette,ref BeanFacette_internal p_facetteASupprimer,bool p_seulementSiFacetteExterne_vf) + { + bool v_parcelleSupprimee_vf=false; + try + { + if (p_seulementSiFacetteExterne_vf && p_facetteASupprimer.p02_arcs.Where(c => c.p20_statutArc == enumStatutArc.arcExterne).Count()==0) + { + return false; + } + List v_arcsSupprimer = p_facetteASupprimer.p02_arcs.Where(c => c.p20_statutArc == enumStatutArc.arcExterne).ToList(); + + List v_arcsNonExternes = p_facetteASupprimer.p02_arcs.Where(c => c.p20_statutArc != enumStatutArc.arcExterne).ToList(); + foreach(BeanArc_internal v_arcNonExterne in v_arcsNonExternes) + { + v_arcNonExterne.p20_statutArc = enumStatutArc.arcExterne; + if (v_arcNonExterne.p21_facetteGauche.p00_idFacette == p_facetteASupprimer.p00_idFacette) + { + v_arcNonExterne.p21_facetteGauche = null; + } + else + { + v_arcNonExterne.p22_facetteDroite = null; + } + } + p_topologieFacette.FacetteSupprimer(p_facetteASupprimer); + List v_hcodesASupprimer=v_arcsSupprimer.Select(c => c.p01_hcodeArc).ToList(); + BeanArc_internal v_arcASupprimer; + foreach (string v_codeArcASupprimer in v_hcodesASupprimer) + { + v_arcASupprimer = p_topologieFacette.p12_arcsByCode[v_codeArcASupprimer]; + p_topologieFacette.ArcSupprimer(v_arcASupprimer); + } + } + catch (Exception) + { + throw; + } + return v_parcelleSupprimee_vf; + } } } \ No newline at end of file diff --git a/DEM.Net.Core/Services/Lab/FLabServices.cs b/DEM.Net.Core/Services/Lab/FLabServices.cs index c0125a42..1759e2b5 100644 --- a/DEM.Net.Core/Services/Lab/FLabServices.cs +++ b/DEM.Net.Core/Services/Lab/FLabServices.cs @@ -49,6 +49,10 @@ public static IGeomorphoServices createGeomorphoServices() { return new GeomorphoServices(); } + public static ICalculServicesVoronoi createVoronoiServices() + { + return new CalculServicesVoronoi(); + } } } diff --git a/DEM.Net.Core/Services/Lab/ICalculServicesVoronoi.cs b/DEM.Net.Core/Services/Lab/ICalculServicesVoronoi.cs new file mode 100644 index 00000000..9c465859 --- /dev/null +++ b/DEM.Net.Core/Services/Lab/ICalculServicesVoronoi.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace DEM.Net.Core.Services.Lab +{ + public interface ICalculServicesVoronoi + { + BeanTopologieFacettes GetTopologieVoronoi(List p_points, int p_srid, double p_ecartMiniEnM); + } +} \ No newline at end of file diff --git a/DEM.Net.Core/Services/Lab/ICalculServices_Medium.cs b/DEM.Net.Core/Services/Lab/ICalculServices_Medium.cs index e79f9585..f80249ae 100644 --- a/DEM.Net.Core/Services/Lab/ICalculServices_Medium.cs +++ b/DEM.Net.Core/Services/Lab/ICalculServices_Medium.cs @@ -48,5 +48,8 @@ public interface ICalculServices_Medium Dictionary GetEtComptePointsDoublonnes(List p_pointsToTest); List GetOrdonnancementPointsFacette(List p_pointsFacettes, bool p_renvoyerNullSiColineaires_vf, bool p_sensHoraireSinonAntiHoraire_vf); List GetOrdonnancementArcsAutourPointFacette(BeanPoint_internal p_pointFacette, int p_idPremierArc, bool p_sensHoraireSinonAntihoraire_vf); - } + void RecalculFacettes(ref BeanTopologieFacettes p_topol); + bool SupprimerUneFacette(ref BeanTopologieFacettes p_topologieFacette, ref BeanFacette_internal p_facetteASupprimer, bool p_seulementSiFacetteExterne_vf); + + } } \ No newline at end of file diff --git a/DEM.Net.Core/Services/Lab/IUtilitairesServices.cs b/DEM.Net.Core/Services/Lab/IUtilitairesServices.cs index 7881ced5..29b9829b 100644 --- a/DEM.Net.Core/Services/Lab/IUtilitairesServices.cs +++ b/DEM.Net.Core/Services/Lab/IUtilitairesServices.cs @@ -31,9 +31,10 @@ namespace DEM.Net.Core.Services.Lab { public interface IUtilitairesServices { - string GethCodeGeogPoint(double[] p_coord, int p_nbreDecimalesMoins1SiToutes=2, char p_separateur = '_'); - string GethCodeGeogObjet(List p_points, int p_nbreDecimalesMoins1SiToutes=2, char p_separateur = '_'); - string GethCodeGeogSegment(double[] p_coord1, double[] p_coord2, int p_nbreDecimalesMoins1SiToutes=2, char p_separateur='_'); + //string GethCodeGeogPoint(double[] p_coord, int p_nbreDecimalesMoins1SiToutes=2, char p_separateur = '_'); + string GetHCodeGeogPoint(double[] p_coordPoint, int p_nbreCaractSignifiants = 10, char p_separateur = '_'); + string GetHCodeGeogPoint(List p_points, int p_nbreCaractSignifiants = 10, char p_separateur = '_'); + string GethCodeGeogSegment(double[] p_coord1, double[] p_coord2, int p_nbreCaractSignifiants = 10, char p_separateur = '_'); // IGeometry GetGeometryArc(BeanArc_internal p_arc, bool ifPt1AndPt2IqualReturnPointElseNull); IGeometry GetGeometryLine(double[] p_coordPoint1, double[] p_coordPoint2, int p_srid, bool ifPt1AndPt2IqualReturnPointElseNull); diff --git a/DEM.Net.Core/Services/Lab/UtilitairesServices.cs b/DEM.Net.Core/Services/Lab/UtilitairesServices.cs index 55e8250e..9c9850e8 100644 --- a/DEM.Net.Core/Services/Lab/UtilitairesServices.cs +++ b/DEM.Net.Core/Services/Lab/UtilitairesServices.cs @@ -39,46 +39,32 @@ public Point ConstructPoint(double x, double y, int srid) { return new Point(x, y) { SRID = srid }; } - public string GethCodeGeogPoint(double[] p_coord, int p_nbreDecimalesMoins1SiToutes, char p_separateur) + + + public string GetHCodeGeogPoint(double[] p_coordPoint, int p_nbreCaractSignifiants = 10, char p_separateur = '_') { - string v_code = ""; - try + string v_composante1 = p_coordPoint[0].ToString(); + if (v_composante1.Length > p_nbreCaractSignifiants) { - if (p_nbreDecimalesMoins1SiToutes >= 0) - { - foreach (double v_c in p_coord) - { - v_code += Math.Round(v_c, p_nbreDecimalesMoins1SiToutes); - v_code += p_separateur; - } - } - else - { - foreach (double v_c in p_coord) - { - v_code += v_c; - v_code += p_separateur; - } - } - v_code = v_code.Substring(0, v_code.Length - 1); + v_composante1 = v_composante1.ToString().Substring(0, p_nbreCaractSignifiants); } - catch (Exception) + string v_composante2 = p_coordPoint[1].ToString(); + if (v_composante2.Length > p_nbreCaractSignifiants) { - - throw; + v_composante2 = v_composante2.ToString().Substring(0, p_nbreCaractSignifiants); } - return v_code; + return v_composante1 + p_separateur + v_composante2; } - public string GethCodeGeogObjet(List p_points, int p_nbreDecimalesMoins1SiToutes, char p_separateur) + public string GetHCodeGeogPoint(List p_points, int p_nbreCaractSignifiants = 10, char p_separateur = '_') { string v_code = ""; try { - List p_pointsOrd=p_points.OrderBy(c => c[0]).ThenBy(c => c[1]).ToList(); + List p_pointsOrd = p_points.OrderBy(c => c[0]).ThenBy(c => c[1]).ToList(); - foreach(double[] v_point in p_pointsOrd) + foreach (double[] v_point in p_pointsOrd) { - v_code += GethCodeGeogPoint(v_point, p_nbreDecimalesMoins1SiToutes, p_separateur); + v_code += GetHCodeGeogPoint(v_point, p_nbreCaractSignifiants, p_separateur); v_code += p_separateur; } v_code = v_code.Substring(0, v_code.Length - 1); @@ -90,7 +76,7 @@ public string GethCodeGeogObjet(List p_points, int p_nbreDecimalesMoin } return v_code; } - public string GethCodeGeogSegment(double[] p_coord1, double[] p_coord2, int p_nbreDecimalesMoins1SiToutes, char p_separateur) + public string GethCodeGeogSegment(double[] p_coord1, double[] p_coord2, int p_nbreCaractSignifiants = 10, char p_separateur = '_') { string v_code = ""; try @@ -99,7 +85,7 @@ public string GethCodeGeogSegment(double[] p_coord1, double[] p_coord2, int p_nb p_points.Add(p_coord1); p_points.Add(p_coord2); // - v_code = GethCodeGeogObjet(p_points, p_nbreDecimalesMoins1SiToutes, p_separateur); + v_code = GetHCodeGeogPoint(p_points, p_nbreCaractSignifiants, p_separateur); } catch (Exception) { @@ -165,6 +151,7 @@ public IGeometry GetGeometryPolygon(List p_coordPointsDuContour, int p } return v_geomArc; } + } } diff --git a/DEM.Net.Core/Services/Lab/Voronoi.cs b/DEM.Net.Core/Services/Lab/Voronoi.cs new file mode 100644 index 00000000..9464842b --- /dev/null +++ b/DEM.Net.Core/Services/Lab/Voronoi.cs @@ -0,0 +1,985 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + + //* Created by SharpDevelop. + //* User: Burhan + //* Date: 17/06/2014 + //* Time: 11:30 م + //* + //* To change this template use Tools | Options | Coding | Edit Standard Headers. + //*/ + + /* + * The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T + * Bell Laboratories. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * This code was originally written by Stephan Fortune in C code. I, Shane O'Sullivan, + * have since modified it, encapsulating it in a C++ class and, fixing memory leaks and + * adding accessors to the Voronoi Edges. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * Java Version by Zhenyu Pan + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + + /* + * C# Version by Burhan Joukhadar + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +using System; +using System.Collections.Generic; + +namespace FruitiereMapsLib.Services.Data +{ + /// + /// Description of Voronoi. + /// + public class Voronoi + { + // ************* Private members ****************** + double borderMinX, borderMaxX, borderMinY, borderMaxY; + int siteidx; + double xmin, xmax, ymin, ymax, deltax, deltay; + int nvertices; + int nedges; + int nsites; + Site[] sites; + Site bottomsite; + int sqrt_nsites; + double minDistanceBetweenSites; + int PQcount; + int PQmin; + int PQhashsize; + Halfedge[] PQhash; + + const int LE = 0; + const int RE = 1; + + int ELhashsize; + Halfedge[] ELhash; + Halfedge ELleftend, ELrightend; + List allEdges; + + + // ************* Public methods ****************** + // ****************************************** + + // constructor + public Voronoi(double minDistanceBetweenSites) + { + siteidx = 0; + sites = null; + + allEdges = null; + this.minDistanceBetweenSites = minDistanceBetweenSites; + } + + /** + * + * @param xValuesIn Array of X values for each site. + * @param yValuesIn Array of Y values for each site. Must be identical length to yValuesIn + * @param minX The minimum X of the bounding box around the voronoi + * @param maxX The maximum X of the bounding box around the voronoi + * @param minY The minimum Y of the bounding box around the voronoi + * @param maxY The maximum Y of the bounding box around the voronoi + * @return + */ + // تستدعى هذه العملية لإنشاء مخطط فورونوي + public List generateVoronoi(double[] xValuesIn, double[] yValuesIn, double minX, double maxX, double minY, double maxY) + { + sort(xValuesIn, yValuesIn, xValuesIn.Length); + + // Check bounding box inputs - if mins are bigger than maxes, swap them + double temp = 0; + if (minX > maxX) + { + temp = minX; + minX = maxX; + maxX = temp; + } + if (minY > maxY) + { + temp = minY; + minY = maxY; + maxY = temp; + } + + borderMinX = minX; + borderMinY = minY; + borderMaxX = maxX; + borderMaxY = maxY; + + siteidx = 0; + voronoi_bd(); + return allEdges; + } + + + /********************************************************* + * Private methods - implementation details + ********************************************************/ + + private void sort(double[] xValuesIn, double[] yValuesIn, int count) + { + sites = null; + allEdges = new List(); + + nsites = count; + nvertices = 0; + nedges = 0; + + double sn = (double)nsites + 4; + sqrt_nsites = (int)Math.Sqrt(sn); + + // Copy the inputs so we don't modify the originals + double[] xValues = new double[count]; + double[] yValues = new double[count]; + for (int i = 0; i < count; i++) + { + xValues[i] = xValuesIn[i]; + yValues[i] = yValuesIn[i]; + } + sortNode(xValues, yValues, count); + } + + private void qsort(Site[] sites) + { + List listSites = new List(sites.Length); + for (int i = 0; i < sites.Length; i++) + { + listSites.Add(sites[i]); + } + + listSites.Sort(new SiteSorterYX()); + + // Copy back into the array + for (int i = 0; i < sites.Length; i++) + { + sites[i] = listSites[i]; + } + } + + private void sortNode(double[] xValues, double[] yValues, int numPoints) + { + nsites = numPoints; + sites = new Site[nsites]; + xmin = xValues[0]; + ymin = yValues[0]; + xmax = xValues[0]; + ymax = yValues[0]; + + for (int i = 0; i < nsites; i++) + { + sites[i] = new Site(); + sites[i].coord.setPoint(xValues[i], yValues[i]); + sites[i].sitenbr = i; + + if (xValues[i] < xmin) + xmin = xValues[i]; + else if (xValues[i] > xmax) + xmax = xValues[i]; + + if (yValues[i] < ymin) + ymin = yValues[i]; + else if (yValues[i] > ymax) + ymax = yValues[i]; + } + + qsort(sites); + deltax = xmax - xmin; + deltay = ymax - ymin; + } + + private Site nextone() + { + Site s; + if (siteidx < nsites) + { + s = sites[siteidx]; + siteidx++; + return s; + } + return null; + } + + private Edge bisect(Site s1, Site s2) + { + double dx, dy, adx, ady; + Edge newedge; + + newedge = new Edge(); + + newedge.reg[0] = s1; + newedge.reg[1] = s2; + + newedge.ep[0] = null; + newedge.ep[1] = null; + + dx = s2.coord.x - s1.coord.x; + dy = s2.coord.y - s1.coord.y; + + adx = dx > 0 ? dx : -dx; + ady = dy > 0 ? dy : -dy; + newedge.c = (double)(s1.coord.x * dx + s1.coord.y * dy + (dx * dx + dy * dy) * 0.5); + + if (adx > ady) + { + newedge.a = 1.0; + newedge.b = dy / dx; + newedge.c /= dx; + } + else + { + newedge.a = dx / dy; + newedge.b = 1.0; + newedge.c /= dy; + } + + newedge.edgenbr = nedges; + nedges++; + + return newedge; + } + + private void makevertex(Site v) + { + v.sitenbr = nvertices; + nvertices++; + } + + private bool PQinitialize() + { + PQcount = 0; + PQmin = 0; + PQhashsize = 4 * sqrt_nsites; + PQhash = new Halfedge[PQhashsize]; + + for (int i = 0; i < PQhashsize; i++) + { + PQhash[i] = new Halfedge(); + } + return true; + } + + private int PQbucket(Halfedge he) + { + int bucket; + + bucket = (int)((he.ystar - ymin) / deltay * PQhashsize); + if (bucket < 0) + bucket = 0; + if (bucket >= PQhashsize) + bucket = PQhashsize - 1; + if (bucket < PQmin) + PQmin = bucket; + + return bucket; + } + + // push the HalfEdge into the ordered linked list of vertices + private void PQinsert(Halfedge he, Site v, double offset) + { + Halfedge last, next; + + he.vertex = v; + he.ystar = (double)(v.coord.y + offset); + last = PQhash[PQbucket(he)]; + + while + ( + (next = last.PQnext) != null + && + (he.ystar > next.ystar || (he.ystar == next.ystar && v.coord.x > next.vertex.coord.x)) + ) + { + last = next; + } + + he.PQnext = last.PQnext; + last.PQnext = he; + PQcount++; + } + + // remove the HalfEdge from the list of vertices + private void PQdelete(Halfedge he) + { + Halfedge last; + + if (he.vertex != null) + { + last = PQhash[PQbucket(he)]; + while (last.PQnext != he) + { + last = last.PQnext; + } + + last.PQnext = he.PQnext; + PQcount--; + he.vertex = null; + } + } + + private bool PQempty() + { + return (PQcount == 0); + } + + private Point PQ_min() + { + Point answer = new Point(); + + while (PQhash[PQmin].PQnext == null) + { + PQmin++; + } + + answer.x = PQhash[PQmin].PQnext.vertex.coord.x; + answer.y = PQhash[PQmin].PQnext.ystar; + return answer; + } + + private Halfedge PQextractmin() + { + Halfedge curr; + + curr = PQhash[PQmin].PQnext; + PQhash[PQmin].PQnext = curr.PQnext; + PQcount--; + + return curr; + } + + private Halfedge HEcreate(Edge e, int pm) + { + Halfedge answer = new Halfedge(); + answer.ELedge = e; + answer.ELpm = pm; + answer.PQnext = null; + answer.vertex = null; + + return answer; + } + + private bool ELinitialize() + { + ELhashsize = 2 * sqrt_nsites; + ELhash = new Halfedge[ELhashsize]; + + for (int i = 0; i < ELhashsize; i++) + { + ELhash[i] = null; + } + + ELleftend = HEcreate(null, 0); + ELrightend = HEcreate(null, 0); + ELleftend.ELleft = null; + ELleftend.ELright = ELrightend; + ELrightend.ELleft = ELleftend; + ELrightend.ELright = null; + ELhash[0] = ELleftend; + ELhash[ELhashsize - 1] = ELrightend; + + return true; + } + + private Halfedge ELright(Halfedge he) + { + return he.ELright; + } + + private Halfedge ELleft(Halfedge he) + { + return he.ELleft; + } + + private Site leftreg(Halfedge he) + { + if (he.ELedge == null) + { + return bottomsite; + } + return (he.ELpm == LE ? he.ELedge.reg[LE] : he.ELedge.reg[RE]); + } + + private void ELinsert(Halfedge lb, Halfedge newHe) + { + newHe.ELleft = lb; + newHe.ELright = lb.ELright; + (lb.ELright).ELleft = newHe; + lb.ELright = newHe; + } + + /* + * This delete routine can't reclaim node, since pointers from hash table + * may be present. + */ + private void ELdelete(Halfedge he) + { + (he.ELleft).ELright = he.ELright; + (he.ELright).ELleft = he.ELleft; + he.deleted = true; + } + + /* Get entry from hash table, pruning any deleted nodes */ + private Halfedge ELgethash(int b) + { + Halfedge he; + if (b < 0 || b >= ELhashsize) + return null; + + he = ELhash[b]; + if (he == null || !he.deleted) + return he; + + /* Hash table points to deleted half edge. Patch as necessary. */ + ELhash[b] = null; + return null; + } + + private Halfedge ELleftbnd(Point p) + { + int bucket; + Halfedge he; + + /* Use hash table to get close to desired halfedge */ + // use the hash function to find the place in the hash map that this + // HalfEdge should be + bucket = (int)((p.x - xmin) / deltax * ELhashsize); + + // make sure that the bucket position is within the range of the hash + // array + if (bucket < 0) bucket = 0; + if (bucket >= ELhashsize) bucket = ELhashsize - 1; + + he = ELgethash(bucket); + + // if the HE isn't found, search backwards and forwards in the hash map + // for the first non-null entry + if (he == null) + { + for (int i = 1; i < ELhashsize; i++) + { + if ((he = ELgethash(bucket - i)) != null) + break; + if ((he = ELgethash(bucket + i)) != null) + break; + } + } + + /* Now search linear list of halfedges for the correct one */ + if (he == ELleftend || (he != ELrightend && right_of(he, p))) + { + // keep going right on the list until either the end is reached, or + // you find the 1st edge which the point isn't to the right of + do + { + he = he.ELright; + } + while (he != ELrightend && right_of(he, p)); + he = he.ELleft; + } + else + // if the point is to the left of the HalfEdge, then search left for + // the HE just to the left of the point + { + do + { + he = he.ELleft; + } + while (he != ELleftend && !right_of(he, p)); + } + + /* Update hash table and reference counts */ + if (bucket > 0 && bucket < ELhashsize - 1) + { + ELhash[bucket] = he; + } + + return he; + } + + private void pushGraphEdge(Site leftSite, Site rightSite, double x1, double y1, double x2, double y2) + { + GraphEdge newEdge = new GraphEdge(); + allEdges.Add(newEdge); + newEdge.x1 = x1; + newEdge.y1 = y1; + newEdge.x2 = x2; + newEdge.y2 = y2; + + newEdge.site1 = leftSite.sitenbr; + newEdge.site2 = rightSite.sitenbr; + } + + private void clip_line(Edge e) + { + double pxmin, pxmax, pymin, pymax; + Site s1, s2; + + double x1 = e.reg[0].coord.x; + double y1 = e.reg[0].coord.y; + double x2 = e.reg[1].coord.x; + double y2 = e.reg[1].coord.y; + double x = x2 - x1; + double y = y2 - y1; + + // if the distance between the two points this line was created from is + // less than the square root of 2 عن جد؟, then ignore it + if (Math.Sqrt((x * x) + (y * y)) < minDistanceBetweenSites) + { + return; + } + pxmin = borderMinX; + pymin = borderMinY; + pxmax = borderMaxX; + pymax = borderMaxY; + + if (e.a == 1.0 && e.b >= 0.0) + { + s1 = e.ep[1]; + s2 = e.ep[0]; + } + else + { + s1 = e.ep[0]; + s2 = e.ep[1]; + } + + if (e.a == 1.0) + { + y1 = pymin; + + if (s1 != null && s1.coord.y > pymin) + y1 = s1.coord.y; + if (y1 > pymax) + y1 = pymax; + x1 = e.c - e.b * y1; + y2 = pymax; + + if (s2 != null && s2.coord.y < pymax) + y2 = s2.coord.y; + if (y2 < pymin) + y2 = pymin; + x2 = e.c - e.b * y2; + if (((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))) + return; + + if (x1 > pxmax) + { + x1 = pxmax; + y1 = (e.c - x1) / e.b; + } + if (x1 < pxmin) + { + x1 = pxmin; + y1 = (e.c - x1) / e.b; + } + if (x2 > pxmax) + { + x2 = pxmax; + y2 = (e.c - x2) / e.b; + } + if (x2 < pxmin) + { + x2 = pxmin; + y2 = (e.c - x2) / e.b; + } + + } + else + { + x1 = pxmin; + if (s1 != null && s1.coord.x > pxmin) + x1 = s1.coord.x; + if (x1 > pxmax) + x1 = pxmax; + y1 = e.c - e.a * x1; + + x2 = pxmax; + if (s2 != null && s2.coord.x < pxmax) + x2 = s2.coord.x; + if (x2 < pxmin) + x2 = pxmin; + y2 = e.c - e.a * x2; + + if (((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))) + return; + + if (y1 > pymax) + { + y1 = pymax; + x1 = (e.c - y1) / e.a; + } + if (y1 < pymin) + { + y1 = pymin; + x1 = (e.c - y1) / e.a; + } + if (y2 > pymax) + { + y2 = pymax; + x2 = (e.c - y2) / e.a; + } + if (y2 < pymin) + { + y2 = pymin; + x2 = (e.c - y2) / e.a; + } + } + + pushGraphEdge(e.reg[0], e.reg[1], x1, y1, x2, y2); + } + + private void endpoint(Edge e, int lr, Site s) + { + e.ep[lr] = s; + if (e.ep[RE - lr] == null) + return; + clip_line(e); + } + + /* returns true if p is to right of halfedge e */ + private bool right_of(Halfedge el, Point p) + { + Edge e; + Site topsite; + bool right_of_site; + bool above, fast; + double dxp, dyp, dxs, t1, t2, t3, yl; + + e = el.ELedge; + topsite = e.reg[1]; + + if (p.x > topsite.coord.x) + right_of_site = true; + else + right_of_site = false; + + if (right_of_site && el.ELpm == LE) + return true; + if (!right_of_site && el.ELpm == RE) + return false; + + if (e.a == 1.0) + { + dxp = p.x - topsite.coord.x; + dyp = p.y - topsite.coord.y; + fast = false; + + if ((!right_of_site & (e.b < 0.0)) | (right_of_site & (e.b >= 0.0))) + { + above = dyp >= e.b * dxp; + fast = above; + } + else + { + above = p.x + p.y * e.b > e.c; + if (e.b < 0.0) + above = !above; + if (!above) + fast = true; + } + if (!fast) + { + dxs = topsite.coord.x - (e.reg[0]).coord.x; + above = e.b * (dxp * dxp - dyp * dyp) + < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b); + + if (e.b < 0) + above = !above; + } + } + else // e.b == 1.0 + { + yl = e.c - e.a * p.x; + t1 = p.y - yl; + t2 = p.x - topsite.coord.x; + t3 = yl - topsite.coord.y; + above = t1 * t1 > t2 * t2 + t3 * t3; + } + return (el.ELpm == LE ? above : !above); + } + + private Site rightreg(Halfedge he) + { + if (he.ELedge == (Edge)null) + // if this halfedge has no edge, return the bottom site (whatever + // that is) + { + return (bottomsite); + } + + // if the ELpm field is zero, return the site 0 that this edge bisects, + // otherwise return site number 1 + return (he.ELpm == LE ? he.ELedge.reg[RE] : he.ELedge.reg[LE]); + } + + private double dist(Site s, Site t) + { + double dx, dy; + dx = s.coord.x - t.coord.x; + dy = s.coord.y - t.coord.y; + return Math.Sqrt(dx * dx + dy * dy); + } + + // create a new site where the HalfEdges el1 and el2 intersect - note that + // the Point in the argument list is not used, don't know why it's there + private Site intersect(Halfedge el1, Halfedge el2) + { + Edge e1, e2, e; + Halfedge el; + double d, xint, yint; + bool right_of_site; + Site v; // vertex + + e1 = el1.ELedge; + e2 = el2.ELedge; + + if (e1 == null || e2 == null) + return null; + + // if the two edges bisect the same parent, return null + if (e1.reg[1] == e2.reg[1]) + return null; + + d = e1.a * e2.b - e1.b * e2.a; + if (-1.0e-10 < d && d < 1.0e-10) + return null; + + xint = (e1.c * e2.b - e2.c * e1.b) / d; + yint = (e2.c * e1.a - e1.c * e2.a) / d; + + if ((e1.reg[1].coord.y < e2.reg[1].coord.y) + || (e1.reg[1].coord.y == e2.reg[1].coord.y && e1.reg[1].coord.x < e2.reg[1].coord.x)) + { + el = el1; + e = e1; + } + else + { + el = el2; + e = e2; + } + + right_of_site = xint >= e.reg[1].coord.x; + if ((right_of_site && el.ELpm == LE) + || (!right_of_site && el.ELpm == RE)) + return null; + + // create a new site at the point of intersection - this is a new vector + // event waiting to happen + v = new Site(); + v.coord.x = xint; + v.coord.y = yint; + return v; + } + + /* + * implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax, deltax, + * deltay (can all be estimates). Performance suffers if they are wrong; + * better to make nsites, deltax, and deltay too big than too small. (?) + */ + private bool voronoi_bd() + { + Site newsite, bot, top, temp, p; + Site v; + Point newintstar = null; + int pm; + Halfedge lbnd, rbnd, llbnd, rrbnd, bisector; + Edge e; + + PQinitialize(); + ELinitialize(); + + bottomsite = nextone(); + newsite = nextone(); + while (true) + { + if (!PQempty()) + { + newintstar = PQ_min(); + } + // if the lowest site has a smaller y value than the lowest vector + // intersection, + // process the site otherwise process the vector intersection + + if (newsite != null && (PQempty() + || newsite.coord.y < newintstar.y + || (newsite.coord.y == newintstar.y + && newsite.coord.x < newintstar.x))) + { + /* new site is smallest -this is a site event */ + // get the first HalfEdge to the LEFT of the new site + lbnd = ELleftbnd((newsite.coord)); + // get the first HalfEdge to the RIGHT of the new site + rbnd = ELright(lbnd); + // if this halfedge has no edge,bot =bottom site (whatever that + // is) + bot = rightreg(lbnd); + // create a new edge that bisects + e = bisect(bot, newsite); + + // create a new HalfEdge, setting its ELpm field to 0 + bisector = HEcreate(e, LE); + // insert this new bisector edge between the left and right + // vectors in a linked list + ELinsert(lbnd, bisector); + + // if the new bisector intersects with the left edge, + // remove the left edge's vertex, and put in the new one + if ((p = intersect(lbnd, bisector)) != null) + { + PQdelete(lbnd); + PQinsert(lbnd, p, dist(p, newsite)); + } + lbnd = bisector; + // create a new HalfEdge, setting its ELpm field to 1 + bisector = HEcreate(e, RE); + // insert the new HE to the right of the original bisector + // earlier in the IF stmt + ELinsert(lbnd, bisector); + + // if this new bisector intersects with the new HalfEdge + if ((p = intersect(bisector, rbnd)) != null) + { + // push the HE into the ordered linked list of vertices + PQinsert(bisector, p, dist(p, newsite)); + } + newsite = nextone(); + } + else if (!PQempty()) + /* intersection is smallest - this is a vector event */ + { + // pop the HalfEdge with the lowest vector off the ordered list + // of vectors + lbnd = PQextractmin(); + // get the HalfEdge to the left of the above HE + llbnd = ELleft(lbnd); + // get the HalfEdge to the right of the above HE + rbnd = ELright(lbnd); + // get the HalfEdge to the right of the HE to the right of the + // lowest HE + rrbnd = ELright(rbnd); + // get the Site to the left of the left HE which it bisects + bot = leftreg(lbnd); + // get the Site to the right of the right HE which it bisects + top = rightreg(rbnd); + + v = lbnd.vertex; // get the vertex that caused this event + makevertex(v); // set the vertex number - couldn't do this + // earlier since we didn't know when it would be processed + endpoint(lbnd.ELedge, lbnd.ELpm, v); + // set the endpoint of + // the left HalfEdge to be this vector + endpoint(rbnd.ELedge, rbnd.ELpm, v); + // set the endpoint of the right HalfEdge to + // be this vector + ELdelete(lbnd); // mark the lowest HE for + // deletion - can't delete yet because there might be pointers + // to it in Hash Map + PQdelete(rbnd); + // remove all vertex events to do with the right HE + ELdelete(rbnd); // mark the right HE for + // deletion - can't delete yet because there might be pointers + // to it in Hash Map + pm = LE; // set the pm variable to zero + + if (bot.coord.y > top.coord.y) + // if the site to the left of the event is higher than the + // Site + { // to the right of it, then swap them and set the 'pm' + // variable to 1 + temp = bot; + bot = top; + top = temp; + pm = RE; + } + e = bisect(bot, top); // create an Edge (or line) + // that is between the two Sites. This creates the formula of + // the line, and assigns a line number to it + bisector = HEcreate(e, pm); // create a HE from the Edge 'e', + // and make it point to that edge + // with its ELedge field + ELinsert(llbnd, bisector); // insert the new bisector to the + // right of the left HE + endpoint(e, RE - pm, v); // set one endpoint to the new edge + // to be the vector point 'v'. + // If the site to the left of this bisector is higher than the + // right Site, then this endpoint + // is put in position 0; otherwise in pos 1 + + // if left HE and the new bisector intersect, then delete + // the left HE, and reinsert it + if ((p = intersect(llbnd, bisector)) != null) + { + PQdelete(llbnd); + PQinsert(llbnd, p, dist(p, bot)); + } + + // if right HE and the new bisector intersect, then + // reinsert it + if ((p = intersect(bisector, rrbnd)) != null) + { + PQinsert(bisector, p, dist(p, bot)); + } + } + else + { + break; + } + } + + for (lbnd = ELright(ELleftend); lbnd != ELrightend; lbnd = ELright(lbnd)) + { + e = lbnd.ELedge; + clip_line(e); + } + + return true; + } + + } // Voronoi Class End +} // namespace Voronoi2 End \ No newline at end of file diff --git a/DEM.Net.Core/Services/Lab/VoronoiElements.cs b/DEM.Net.Core/Services/Lab/VoronoiElements.cs new file mode 100644 index 00000000..8be5bcbb --- /dev/null +++ b/DEM.Net.Core/Services/Lab/VoronoiElements.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +/* + * Created by SharpDevelop. + * User: Burhan + * Date: 17/06/2014 + * Time: 09:29 م + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +/* + Copyright 2011 James Humphreys. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of James Humphreys. + */ + +/* + * C# Version by Burhan Joukhadar + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + + +using System; +using System.Collections.Generic; + +namespace FruitiereMapsLib.Services.Data +{ + public class Point + { + public double x, y; + + public Point() + { + } + + public void setPoint(double x, double y) + { + this.x = x; + this.y = y; + } + } + + // use for sites and vertecies + public class Site + { + public Point coord; + public int sitenbr; + + public Site() + { + coord = new Point(); + } + } + + public class Edge + { + public double a = 0, b = 0, c = 0; + public Site[] ep; + public Site[] reg; + public int edgenbr; + + public Edge() + { + ep = new Site[2]; + reg = new Site[2]; + } + } + + + public class Halfedge + { + public Halfedge ELleft, ELright; + public Edge ELedge; + public bool deleted; + public int ELpm; + public Site vertex; + public double ystar; + public Halfedge PQnext; + + public Halfedge() + { + PQnext = null; + } + } + + public class GraphEdge + { + public double x1, y1, x2, y2; + public int site1, site2; + } + + // للترتيب + public class SiteSorterYX : IComparer + { + public int Compare(Site p1, Site p2) + { + Point s1 = p1.coord; + Point s2 = p2.coord; + if (s1.y < s2.y) return -1; + if (s1.y > s2.y) return 1; + if (s1.x < s2.x) return -1; + if (s1.x > s2.x) return 1; + return 0; + } + } +} diff --git a/DEM.Net.Core/Services/Lab/enumServices.cs b/DEM.Net.Core/Services/Lab/enumServices.cs index a505ecec..4be313e1 100644 --- a/DEM.Net.Core/Services/Lab/enumServices.cs +++ b/DEM.Net.Core/Services/Lab/enumServices.cs @@ -83,4 +83,10 @@ public enum enum_qualificationMorpho_arc autre, indetermine } + public enum enumEtatDesTraitementsBords + { + indetermine, + unCote, + deuxCotes + } } diff --git a/DEM.Net.TestWinForm/EchantillonsTestsServices.cs b/DEM.Net.TestWinForm/EchantillonsTestsServices.cs index c7aeccc9..9eeaeea7 100644 --- a/DEM.Net.TestWinForm/EchantillonsTestsServices.cs +++ b/DEM.Net.TestWinForm/EchantillonsTestsServices.cs @@ -109,7 +109,8 @@ internal List GetPointsTestsXY_RepartitionUniforme(BeanParam v_coord[2] = 0; // BeanPoint_internal v_point = new BeanPoint_internal(v_coord, p_paramGenerationPointsTest.p10_srid); - p_code = FLabServices.createUtilitaires().GethCodeGeogPoint(v_coord); + // p_code = FLabServices.createUtilitaires().GethCodeGeogPoint(v_coord); + p_code = FLabServices.createUtilitaires().GetHCodeGeogPoint(v_coord); //(On évite les doublons) if (!p_codes.Contains(p_code)) { From 77bc0854902c17ea36b0be3734665ec0f5bdc6e8 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Wed, 12 Jun 2019 23:35:20 +0200 Subject: [PATCH 03/10] preparing beta 12 --- DEM.Net.Core/DEM.Net.Core.csproj | 8 ++++---- DEM.Net.glTF/DEM.Net.glTF.csproj | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DEM.Net.Core/DEM.Net.Core.csproj b/DEM.Net.Core/DEM.Net.Core.csproj index 42f2c810..0e10345d 100644 --- a/DEM.Net.Core/DEM.Net.Core.csproj +++ b/DEM.Net.Core/DEM.Net.Core.csproj @@ -3,14 +3,12 @@ netstandard2.0;net461 DEM.Net.Core - 0.1.0-beta0011 + 0.1.0-beta0012 Xavier Fischer, Frédéric Aubin Xavier Fischer, Frédéric Aubin Xavier Fischer https://github.com/dem-net/DEM.Net - Dataset reports return a simpler list -TIN fixed when border points were local summits -GPX track can be drawn on 3D model's texture + Symbols package generated alongside DEM, Terrain, Elevation DEM.Net DEM.Net @@ -24,6 +22,8 @@ GPX track can be drawn on 3D model's texture + true + snupkg true true diff --git a/DEM.Net.glTF/DEM.Net.glTF.csproj b/DEM.Net.glTF/DEM.Net.glTF.csproj index 3863a052..d872c171 100644 --- a/DEM.Net.glTF/DEM.Net.glTF.csproj +++ b/DEM.Net.glTF/DEM.Net.glTF.csproj @@ -3,12 +3,12 @@ netstandard2.0;net461 DEM.Net.glTF - 0.1.0-beta0011 + 0.1.0-beta0012 Xavier Fischer Xavier Fischer Xavier Fischer https://github.com/dem-net/DEM.Net.glTF - Updated to DEM.Net.Core 0.1.0-beta0011 + Symbols package generated alongside DEM, Terrain, Elevation, Mesh, 3D, glTF, Map, STL DEM.Net DEM.Net @@ -22,6 +22,8 @@ + true + snupkg true true From 1484b7010666acf0e243977623db2daf6d400a93 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 22:12:50 +0200 Subject: [PATCH 04/10] Points extensions --- DEM.Net.Core/Helpers/PointsExtensions.cs | 33 +++++++++++++++++++ .../Services/DouglasPeuckerReduction.cs | 2 +- DEM.Net.Core/Services/GeometryService.cs | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 DEM.Net.Core/Helpers/PointsExtensions.cs diff --git a/DEM.Net.Core/Helpers/PointsExtensions.cs b/DEM.Net.Core/Helpers/PointsExtensions.cs new file mode 100644 index 00000000..3b0d0ec6 --- /dev/null +++ b/DEM.Net.Core/Helpers/PointsExtensions.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DEM.Net.Core +{ + public static class PointsExtensions + { + /// + /// Get usual stats from an elevated point list. + /// Min/Max altitudes, length, climb/descent + /// Assumes that the provided list is a "line string" + /// + /// + /// + public static ElevationMetrics ComputeMetrics(this IList points) + { + return GeometryService.ComputeMetrics(points); + } + + /// + /// Reduces the point list using a Douglas Peucker Reduction + /// All details below toleranceMeters will usually be skipped + /// + /// + /// + /// + public static List Simplify(this IReadOnlyList points, double toleranceMeters) + { + return DouglasPeucker.DouglasPeuckerReduction(points, toleranceMeters); + } + } +} diff --git a/DEM.Net.Core/Services/DouglasPeuckerReduction.cs b/DEM.Net.Core/Services/DouglasPeuckerReduction.cs index c247d8db..57353630 100644 --- a/DEM.Net.Core/Services/DouglasPeuckerReduction.cs +++ b/DEM.Net.Core/Services/DouglasPeuckerReduction.cs @@ -34,7 +34,7 @@ namespace DEM.Net.Core // // Code from https://gist.github.com/oliverheilig/7777382 // - public static class DouglasPeucker + internal static class DouglasPeucker { /// /// Uses the Douglas Peucker algorithim to reduce the number of points. diff --git a/DEM.Net.Core/Services/GeometryService.cs b/DEM.Net.Core/Services/GeometryService.cs index b1540bb4..c21d9ce2 100644 --- a/DEM.Net.Core/Services/GeometryService.cs +++ b/DEM.Net.Core/Services/GeometryService.cs @@ -186,7 +186,7 @@ public static bool LineLineIntersection(out GeoPoint intersection, GeoSegment li /// /// Input list of points /// object - public static ElevationMetrics ComputeMetrics(List points) + internal static ElevationMetrics ComputeMetrics(IList points) { ElevationMetrics metrics = new ElevationMetrics(); double total = 0; From 239878cd2fe72c85ec2f67221208de7f0e8372ba Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 23:12:49 +0200 Subject: [PATCH 05/10] duplicate namespaces --- DEM.Net.Core/Services/Lab/Voronoi.cs | 2 -- DEM.Net.Core/Services/Lab/VoronoiElements.cs | 3 --- 2 files changed, 5 deletions(-) diff --git a/DEM.Net.Core/Services/Lab/Voronoi.cs b/DEM.Net.Core/Services/Lab/Voronoi.cs index 9464842b..3aa5620d 100644 --- a/DEM.Net.Core/Services/Lab/Voronoi.cs +++ b/DEM.Net.Core/Services/Lab/Voronoi.cs @@ -69,8 +69,6 @@ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */ -using System; -using System.Collections.Generic; namespace FruitiereMapsLib.Services.Data { diff --git a/DEM.Net.Core/Services/Lab/VoronoiElements.cs b/DEM.Net.Core/Services/Lab/VoronoiElements.cs index 8be5bcbb..edd22fa1 100644 --- a/DEM.Net.Core/Services/Lab/VoronoiElements.cs +++ b/DEM.Net.Core/Services/Lab/VoronoiElements.cs @@ -56,9 +56,6 @@ The views and conclusions contained in the software and documentation are those */ -using System; -using System.Collections.Generic; - namespace FruitiereMapsLib.Services.Data { public class Point From 28d9f559be39444f6240ec81a1cb7ec470a944f0 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 23:13:37 +0200 Subject: [PATCH 06/10] GDALVRTFileService is now a singleton with its own cache --- .../IServiceCollectionExtensions.cs | 1 + .../Services/Raster/GDALVRTFileService.cs | 182 +++++++++--------- .../Services/Raster/IGDALVRTFileService.cs | 6 +- DEM.Net.Core/Services/Raster/RasterService.cs | 26 +-- 4 files changed, 110 insertions(+), 105 deletions(-) diff --git a/DEM.Net.Core/DependencyInjection/IServiceCollectionExtensions.cs b/DEM.Net.Core/DependencyInjection/IServiceCollectionExtensions.cs index b4f527f0..2352633d 100644 --- a/DEM.Net.Core/DependencyInjection/IServiceCollectionExtensions.cs +++ b/DEM.Net.Core/DependencyInjection/IServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ public static class IServiceCollectionExtension public static IServiceCollection AddDemNetCore(this IServiceCollection services) { services + .AddSingleton() .AddSingleton() .AddTransient() .AddTransient() diff --git a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs index 8a7e1996..4b448811 100644 --- a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs +++ b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs @@ -25,8 +25,8 @@ using Microsoft.Extensions.Logging; using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -44,54 +44,53 @@ namespace DEM.Net.Core /// public class GDALVRTFileService : IGDALVRTFileService { - private bool _useMemCache = false; - Dictionary> _cacheByDemName; - private readonly string _localDirectory; - private string _vrtFileName; - private Uri _remoteVrtUri; - private Uri _localVrtUri; - private DEMDataSet _dataSet; private const int MAX_AGE_DAYS = 30; - public DEMDataSet Dataset { get { return _dataSet; } } + private readonly ILogger _logger; + private ConcurrentDictionary> _cacheByDemName; - public GDALVRTFileService(string localDirectory, DEMDataSet dataSet) + public GDALVRTFileService(ILogger logger) { - _localDirectory = localDirectory; - _dataSet = dataSet; - _cacheByDemName = new Dictionary>(); + _logger = logger; + _cacheByDemName = new ConcurrentDictionary>(); } /// /// Ensures local directories are created and download VRT file if needed /// - public void Setup(bool useMemoryCache) + public void Setup(DEMDataSet dataSet, string dataSetLocalDir) { try { - _useMemCache = useMemoryCache; - if (_dataSet == null) + if (dataSet == null) throw new ArgumentNullException("Dataset is null."); - Trace.TraceInformation($"Setup for {_dataSet.Name} dataset."); + if (_cacheByDemName.ContainsKey(dataSet.Name)) + return; - if (!Directory.Exists(_localDirectory)) + _logger.LogInformation($"Setup for {dataSet.Name} dataset."); + + if (!Directory.Exists(dataSetLocalDir)) { - Directory.CreateDirectory(_localDirectory); + Directory.CreateDirectory(dataSetLocalDir); } - _vrtFileName = Path.Combine(_localDirectory, UrlHelper.GetFileNameFromUrl(_dataSet.VRTFileUrl)); - _localVrtUri = new Uri(Path.GetFullPath(_vrtFileName), UriKind.Absolute); - _remoteVrtUri = new Uri(_dataSet.VRTFileUrl, UriKind.Absolute); + string vrtFileName = Path.Combine(dataSetLocalDir, UrlHelper.GetFileNameFromUrl(dataSet.VRTFileUrl)); + Uri localVrtUri = new Uri(Path.GetFullPath(vrtFileName), UriKind.Absolute); + Uri remoteVrtUri = new Uri(dataSet.VRTFileUrl, UriKind.Absolute); bool download = true; - if (File.Exists(_vrtFileName)) + if (File.Exists(vrtFileName)) { // Download if too old file - if ((DateTime.Now - File.GetLastWriteTime(_vrtFileName)).TotalDays > MAX_AGE_DAYS) + if ((DateTime.Now - File.GetLastWriteTime(vrtFileName)).TotalDays > MAX_AGE_DAYS) + { + _logger.LogInformation("VRT file is too old."); + } + else if (IsCorrupted(vrtFileName)) { - Trace.TraceInformation("VRT file is too old."); + _logger.LogInformation("VRT file is corrupted."); } else { @@ -101,14 +100,14 @@ public void Setup(bool useMemoryCache) if (download) { - Trace.TraceInformation($"Downloading file from {_dataSet.VRTFileUrl}..."); + _logger.LogInformation($"Downloading index file from {dataSet.VRTFileUrl}... This file will be downloaded once and stored locally."); using (HttpClient client = new HttpClient()) { - using (HttpResponseMessage response = client.GetAsync(_dataSet.VRTFileUrl).Result) + using (HttpResponseMessage response = client.GetAsync(dataSet.VRTFileUrl).Result) { - using (FileStream fs = new FileStream(_vrtFileName, FileMode.Create, FileAccess.Write)) + using (FileStream fs = new FileStream(vrtFileName, FileMode.Create, FileAccess.Write)) { - var contentbytes = client.GetByteArrayAsync(_dataSet.VRTFileUrl).Result; + var contentbytes = client.GetByteArrayAsync(dataSet.VRTFileUrl).Result; fs.Write(contentbytes, 0, contentbytes.Length); } } @@ -120,28 +119,31 @@ public void Setup(bool useMemoryCache) } // Cache - if (_useMemCache) - { - if (_cacheByDemName == null) - { - _cacheByDemName = new Dictionary>(); - } - if (_cacheByDemName.ContainsKey(_vrtFileName) == false) - { - _cacheByDemName[_vrtFileName] = this.Sources().ToList(); - } + if (_cacheByDemName == null) + { + _cacheByDemName = new ConcurrentDictionary>(); } + if (_cacheByDemName.ContainsKey(vrtFileName) == false) + { + _cacheByDemName[dataSet.Name] = this.GetSources(vrtFileName, localVrtUri, remoteVrtUri).ToList(); + } + } catch (Exception ex) { - Trace.TraceError("Unhandled exception: " + ex.Message); - Trace.TraceInformation(ex.ToString()); + _logger.LogError("Unhandled exception: " + ex.Message); + _logger.LogInformation(ex.ToString()); throw; } } + private bool IsCorrupted(string vrtFileName) + { + return false; + } + private double[] _geoTransform; private Dictionary _properties; /// @@ -149,70 +151,78 @@ public void Setup(bool useMemoryCache) /// Supports only VRTRasterBand with ComplexSource or SimpleSource /// /// - public IEnumerable Sources() + public IEnumerable Sources(DEMDataSet dataSet) { - if (_useMemCache && _cacheByDemName.ContainsKey(_vrtFileName)) + if (_cacheByDemName.ContainsKey(dataSet.Name)) { - foreach (var item in _cacheByDemName[_vrtFileName]) + foreach (var item in _cacheByDemName[dataSet.Name]) { yield return item; } } else { - // Create an XmlReader - using (FileStream fileStream = new FileStream(_vrtFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - using (XmlReader reader = XmlReader.Create(fileStream)) + throw new Exception("Must call Init(dataSet) first !"); + } + + } + + private IEnumerable GetSources(string vrtFileName, Uri localUri, Uri remoteUri) + { + + // Create an XmlReader + using (FileStream fileStream = new FileStream(vrtFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (XmlReader reader = XmlReader.Create(fileStream)) + { + if (reader.ReadToFollowing("GeoTransform")) { - if (reader.ReadToFollowing("GeoTransform")) - { - _geoTransform = ParseGeoTransform(reader.ReadElementContentAsString()); - } - else - throw new Exception("GeoTransform element not found!"); + _geoTransform = ParseGeoTransform(reader.ReadElementContentAsString()); + } + else + throw new Exception("GeoTransform element not found!"); - string sourceName = ""; - if (reader.ReadToFollowing("VRTRasterBand")) + string sourceName = ""; + if (reader.ReadToFollowing("VRTRasterBand")) + { + _properties = new Dictionary(); + while (reader.Read()) { - _properties = new Dictionary(); - while (reader.Read()) + if (reader.NodeType == XmlNodeType.Element) { - if (reader.NodeType == XmlNodeType.Element) + if (reader.Name == "ComplexSource" || reader.Name == "SimpleSource") { - if (reader.Name == "ComplexSource" || reader.Name == "SimpleSource") - { - sourceName = reader.Name; - break; - } - _properties[reader.Name] = reader.ReadElementContentAsString(); + sourceName = reader.Name; + break; } + _properties[reader.Name] = reader.ReadElementContentAsString(); } + } - bool isOnFirstSource = true; - while (isOnFirstSource || reader.ReadToFollowing(sourceName)) - { - GDALSource source = ParseGDALSource(reader); - - // SetLocalFileName - source.SourceFileNameAbsolute = new Uri(_remoteVrtUri, source.SourceFileName).ToString(); - source.LocalFileName = new Uri(_localVrtUri, source.SourceFileName).LocalPath; - - // Transform origin - // Xp = padfTransform[0] + P * padfTransform[1] + L * padfTransform[2]; - // Yp = padfTransform[3] + P * padfTransform[4] + L * padfTransform[5]; - source.OriginLon = _geoTransform[0] + source.DstxOff * _geoTransform[1] + source.DstyOff * _geoTransform[2]; - source.OriginLat = _geoTransform[3] + source.DstxOff * _geoTransform[4] + source.DstyOff * _geoTransform[5]; - source.DestLon = _geoTransform[0] + (source.DstxOff + source.DstxSize) * _geoTransform[1] + (source.DstyOff + source.DstySize) * _geoTransform[2]; - source.DestLat = _geoTransform[3] + (source.DstxOff + source.DstxSize) * _geoTransform[4] + (source.DstyOff + source.DstySize) * _geoTransform[5]; - source.BBox = new BoundingBox(source.OriginLon, source.DestLon, source.DestLat, source.OriginLat); - isOnFirstSource = false; - - yield return source; - } + bool isOnFirstSource = true; + while (isOnFirstSource || reader.ReadToFollowing(sourceName)) + { + GDALSource source = ParseGDALSource(reader); + + // SetLocalFileName + source.SourceFileNameAbsolute = new Uri(remoteUri, source.SourceFileName).ToString(); + source.LocalFileName = new Uri(localUri, source.SourceFileName).LocalPath; + + // Transform origin + // Xp = padfTransform[0] + P * padfTransform[1] + L * padfTransform[2]; + // Yp = padfTransform[3] + P * padfTransform[4] + L * padfTransform[5]; + source.OriginLon = _geoTransform[0] + source.DstxOff * _geoTransform[1] + source.DstyOff * _geoTransform[2]; + source.OriginLat = _geoTransform[3] + source.DstxOff * _geoTransform[4] + source.DstyOff * _geoTransform[5]; + source.DestLon = _geoTransform[0] + (source.DstxOff + source.DstxSize) * _geoTransform[1] + (source.DstyOff + source.DstySize) * _geoTransform[2]; + source.DestLat = _geoTransform[3] + (source.DstxOff + source.DstxSize) * _geoTransform[4] + (source.DstyOff + source.DstySize) * _geoTransform[5]; + source.BBox = new BoundingBox(source.OriginLon, source.DestLon, source.DestLat, source.OriginLat); + isOnFirstSource = false; + + yield return source; } } } + } private GDALSource ParseGDALSource(XmlReader reader) @@ -304,7 +314,7 @@ private GDALSource ParseGDALSource(XmlReader reader) } catch (Exception ex) { - Trace.TraceError($"Error parsing GDAL source: {ex.Message}"); + _logger.LogError($"Error parsing GDAL source: {ex.Message}"); } return source; diff --git a/DEM.Net.Core/Services/Raster/IGDALVRTFileService.cs b/DEM.Net.Core/Services/Raster/IGDALVRTFileService.cs index 59dc648b..a589e768 100644 --- a/DEM.Net.Core/Services/Raster/IGDALVRTFileService.cs +++ b/DEM.Net.Core/Services/Raster/IGDALVRTFileService.cs @@ -4,8 +4,8 @@ namespace DEM.Net.Core { public interface IGDALVRTFileService { - DEMDataSet Dataset { get; } - void Setup(bool useMemoryCache); - IEnumerable Sources(); + void Setup(DEMDataSet dataset, string dataSetLocalDir); + + IEnumerable Sources(DEMDataSet dataset); } } \ No newline at end of file diff --git a/DEM.Net.Core/Services/Raster/RasterService.cs b/DEM.Net.Core/Services/Raster/RasterService.cs index ee619634..11850080 100644 --- a/DEM.Net.Core/Services/Raster/RasterService.cs +++ b/DEM.Net.Core/Services/Raster/RasterService.cs @@ -36,11 +36,11 @@ namespace DEM.Net.Core { - public class RasterService : IRasterService + public class RasterService : IRasterService { const string APP_NAME = "DEM.Net"; const string MANIFEST_DIR = "manifest"; - GDALVRTFileService _gdalService; + private readonly IGDALVRTFileService _gdalVrtService; private readonly ILogger _logger; private readonly string _localDirectory; @@ -51,9 +51,10 @@ public string LocalDirectory get { return _localDirectory; } } - public RasterService(ILogger logger = null) + public RasterService(IGDALVRTFileService gdalVrtService, ILogger logger = null) { _logger = logger; + _gdalVrtService = gdalVrtService; //_localDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APP_NAME); _localDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APP_NAME); if (!Directory.Exists(_localDirectory)) @@ -300,13 +301,10 @@ bool BoundingBoxIntersects(BoundingBox bbox1, double lat, double lon) public List GenerateReport(DEMDataSet dataSet, BoundingBox bbox = null) { List statusByFile = new List(); - if (_gdalService == null || _gdalService.Dataset.Name != dataSet.Name) - { - _gdalService = new GDALVRTFileService(GetLocalDEMPath(dataSet), dataSet); - _gdalService.Setup(true); - } - foreach (GDALSource source in _gdalService.Sources()) + _gdalVrtService.Setup(dataSet, GetLocalDEMPath(dataSet)); + + foreach (GDALSource source in _gdalVrtService.Sources(dataSet)) { if (bbox == null || BoundingBoxIntersects(source.BBox, bbox)) @@ -331,13 +329,9 @@ public List GenerateReport(DEMDataSet dataSet, BoundingBox bbox = public DemFileReport GenerateReportForLocation(DEMDataSet dataSet, double lat, double lon) { - if (_gdalService == null || _gdalService.Dataset.Name != dataSet.Name) - { - _gdalService = new GDALVRTFileService(GetLocalDEMPath(dataSet), dataSet); - _gdalService.Setup(true); - } + _gdalVrtService.Setup(dataSet, GetLocalDEMPath(dataSet)); - foreach (GDALSource source in _gdalService.Sources()) + foreach (GDALSource source in _gdalVrtService.Sources(dataSet)) { if (BoundingBoxIntersects(source.BBox, lat, lon)) @@ -360,7 +354,7 @@ public DemFileReport GenerateReportForLocation(DEMDataSet dataSet, double lat, d return null; } - + } From 46195d8aaeb901abead0adf9ea1ecc5329fa80d2 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 23:20:09 +0200 Subject: [PATCH 07/10] optionnal logger --- .../Services/Raster/GDALVRTFileService.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs index 4b448811..dbc46739 100644 --- a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs +++ b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs @@ -49,7 +49,7 @@ public class GDALVRTFileService : IGDALVRTFileService private readonly ILogger _logger; private ConcurrentDictionary> _cacheByDemName; - public GDALVRTFileService(ILogger logger) + public GDALVRTFileService(ILogger logger = null) { _logger = logger; _cacheByDemName = new ConcurrentDictionary>(); @@ -69,7 +69,7 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) if (_cacheByDemName.ContainsKey(dataSet.Name)) return; - _logger.LogInformation($"Setup for {dataSet.Name} dataset."); + _logger?.LogInformation($"Setup for {dataSet.Name} dataset."); if (!Directory.Exists(dataSetLocalDir)) { @@ -86,11 +86,11 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) // Download if too old file if ((DateTime.Now - File.GetLastWriteTime(vrtFileName)).TotalDays > MAX_AGE_DAYS) { - _logger.LogInformation("VRT file is too old."); + _logger?.LogInformation("VRT file is too old."); } else if (IsCorrupted(vrtFileName)) { - _logger.LogInformation("VRT file is corrupted."); + _logger?.LogInformation("VRT file is corrupted."); } else { @@ -100,7 +100,7 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) if (download) { - _logger.LogInformation($"Downloading index file from {dataSet.VRTFileUrl}... This file will be downloaded once and stored locally."); + _logger?.LogInformation($"Downloading index file from {dataSet.VRTFileUrl}... This file will be downloaded once and stored locally."); using (HttpClient client = new HttpClient()) { using (HttpResponseMessage response = client.GetAsync(dataSet.VRTFileUrl).Result) @@ -133,8 +133,8 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) } catch (Exception ex) { - _logger.LogError("Unhandled exception: " + ex.Message); - _logger.LogInformation(ex.ToString()); + _logger?.LogError("Unhandled exception: " + ex.Message); + _logger?.LogInformation(ex.ToString()); throw; } } @@ -314,7 +314,7 @@ private GDALSource ParseGDALSource(XmlReader reader) } catch (Exception ex) { - _logger.LogError($"Error parsing GDAL source: {ex.Message}"); + _logger?.LogError($"Error parsing GDAL source: {ex.Message}"); } return source; From 9bd6deb90bc1888ebe07b3f4ad04d65784b2ec21 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 23:20:32 +0200 Subject: [PATCH 08/10] updated tests for gdal singleton service --- DEM.Net.xUnit.Test/DatasetTests.cs | 17 ++++++++--------- DEM.Net.xUnit.Test/ElevationTests.cs | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/DEM.Net.xUnit.Test/DatasetTests.cs b/DEM.Net.xUnit.Test/DatasetTests.cs index ddd974d8..d43371c1 100644 --- a/DEM.Net.xUnit.Test/DatasetTests.cs +++ b/DEM.Net.xUnit.Test/DatasetTests.cs @@ -12,12 +12,14 @@ public class DatasetTests : IClassFixture { readonly IRasterService _rasterService; readonly IElevationService _elevationService; + readonly IGDALVRTFileService _gdalService; public DatasetTests(DemNetFixture fixture) { _rasterService = fixture.ServiceProvider.GetService(); _elevationService = fixture.ServiceProvider.GetService(); + _gdalService = fixture.ServiceProvider.GetService(); } @@ -26,20 +28,18 @@ public void DatasetTest_SRTM_GL1() { DEMDataSet dataset = DEMDataSet.SRTM_GL1; - GDALVRTFileService vrtService = new GDALVRTFileService(_rasterService.GetLocalDEMPath(dataset), dataset); - vrtService.Setup(false); + _gdalService.Setup(dataset, _rasterService.GetLocalDEMPath(dataset)); - Assert.True(vrtService.Sources().Any()); + Assert.True(_gdalService.Sources(dataset).Any()); } [Fact, TestPriority(1)] public void DatasetTest_SRTM_GL3() { DEMDataSet dataset = DEMDataSet.SRTM_GL3; - GDALVRTFileService vrtService = new GDALVRTFileService(_rasterService.GetLocalDEMPath(dataset), dataset); - vrtService.Setup(false); + _gdalService.Setup(dataset, _rasterService.GetLocalDEMPath(dataset)); - Assert.True(vrtService.Sources().Any()); + Assert.True(_gdalService.Sources(dataset).Any()); } @@ -47,10 +47,9 @@ public void DatasetTest_SRTM_GL3() public void DatasetTest_AW3D() { DEMDataSet dataset = DEMDataSet.AW3D30; - GDALVRTFileService vrtService = new GDALVRTFileService(_rasterService.GetLocalDEMPath(dataset), dataset); - vrtService.Setup(false); + _gdalService.Setup(dataset, _rasterService.GetLocalDEMPath(dataset)); - Assert.True(vrtService.Sources().Any()); + Assert.True(_gdalService.Sources(dataset).Any()); } diff --git a/DEM.Net.xUnit.Test/ElevationTests.cs b/DEM.Net.xUnit.Test/ElevationTests.cs index c7008568..800e2726 100644 --- a/DEM.Net.xUnit.Test/ElevationTests.cs +++ b/DEM.Net.xUnit.Test/ElevationTests.cs @@ -54,7 +54,7 @@ public void TestElevationLine(string dataSetName, double latStart, double lonSta Assert.NotNull(geoPoints); Assert.Equal(expectedPointCount, geoPoints.Count); - var metrics = GeometryService.ComputeMetrics(geoPoints); + var metrics = geoPoints.ComputeMetrics(); Assert.NotNull(metrics); Assert.Equal(expectedClimb, metrics.Climb); Assert.Equal(expectedDescent, metrics.Descent); From ef9984d0e6903428d29ce4541e12f7bbac2eafe9 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Thu, 13 Jun 2019 23:30:44 +0200 Subject: [PATCH 09/10] Check for VRT file corruption --- .../Services/Raster/GDALVRTFileService.cs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs index dbc46739..25893ad8 100644 --- a/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs +++ b/DEM.Net.Core/Services/Raster/GDALVRTFileService.cs @@ -77,8 +77,7 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) } string vrtFileName = Path.Combine(dataSetLocalDir, UrlHelper.GetFileNameFromUrl(dataSet.VRTFileUrl)); - Uri localVrtUri = new Uri(Path.GetFullPath(vrtFileName), UriKind.Absolute); - Uri remoteVrtUri = new Uri(dataSet.VRTFileUrl, UriKind.Absolute); + bool download = true; if (File.Exists(vrtFileName)) @@ -88,7 +87,7 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) { _logger?.LogInformation("VRT file is too old."); } - else if (IsCorrupted(vrtFileName)) + else if (IsCorrupted(dataSet, vrtFileName)) { _logger?.LogInformation("VRT file is corrupted."); } @@ -126,7 +125,7 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) } if (_cacheByDemName.ContainsKey(vrtFileName) == false) { - _cacheByDemName[dataSet.Name] = this.GetSources(vrtFileName, localVrtUri, remoteVrtUri).ToList(); + _cacheByDemName[dataSet.Name] = this.GetSources(dataSet, vrtFileName).ToList(); } @@ -139,9 +138,19 @@ public void Setup(DEMDataSet dataSet, string dataSetLocalDir) } } - private bool IsCorrupted(string vrtFileName) + private bool IsCorrupted(DEMDataSet dataSet, string vrtFileName) { - return false; + bool ok = false; + try + { + ok = !this.GetSources(dataSet, vrtFileName).Any(); + } + catch (Exception ex) + { + _logger?.LogWarning(ex, $"VRT file corrupted: {ex.Message}"); + ok = true; + } + return ok; } private double[] _geoTransform; @@ -167,8 +176,10 @@ public IEnumerable Sources(DEMDataSet dataSet) } - private IEnumerable GetSources(string vrtFileName, Uri localUri, Uri remoteUri) + private IEnumerable GetSources(DEMDataSet dataSet, string vrtFileName) { + Uri localVrtUri = new Uri(Path.GetFullPath(vrtFileName), UriKind.Absolute); + Uri remoteVrtUri = new Uri(dataSet.VRTFileUrl, UriKind.Absolute); // Create an XmlReader using (FileStream fileStream = new FileStream(vrtFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) @@ -204,8 +215,8 @@ private IEnumerable GetSources(string vrtFileName, Uri localUri, Uri GDALSource source = ParseGDALSource(reader); // SetLocalFileName - source.SourceFileNameAbsolute = new Uri(remoteUri, source.SourceFileName).ToString(); - source.LocalFileName = new Uri(localUri, source.SourceFileName).LocalPath; + source.SourceFileNameAbsolute = new Uri(remoteVrtUri, source.SourceFileName).ToString(); + source.LocalFileName = new Uri(localVrtUri, source.SourceFileName).LocalPath; // Transform origin // Xp = padfTransform[0] + P * padfTransform[1] + L * padfTransform[2]; From 5aa9644c5429e5219103c04caf81ef3b403c7755 Mon Sep 17 00:00:00 2001 From: Xavier Fischer Date: Fri, 14 Jun 2019 21:52:49 +0200 Subject: [PATCH 10/10] Update DEM.Net.Core.csproj --- DEM.Net.Core/DEM.Net.Core.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DEM.Net.Core/DEM.Net.Core.csproj b/DEM.Net.Core/DEM.Net.Core.csproj index 0e10345d..c7533138 100644 --- a/DEM.Net.Core/DEM.Net.Core.csproj +++ b/DEM.Net.Core/DEM.Net.Core.csproj @@ -8,7 +8,11 @@ Xavier Fischer, Frédéric Aubin Xavier Fischer https://github.com/dem-net/DEM.Net - Symbols package generated alongside + Symbols package generated alongside +Check for VRT file corruption +Updated tests for gdal singleton service +GDALVRTFileService is now a singleton with its own cache +Point list extensions DEM, Terrain, Elevation DEM.Net DEM.Net