} // Unused but supported as part of the TML file #endregion #region Private Methods /// <summary> /// Tries to load the metadata resource at Path as a text string /// </summary> /// <remarks> /// This method will not throw an exception. /// Side effects: /// It will set ErrorMessage to an error message (if any are encountered) /// It may change the Type and Format if they are wrong. /// </remarks> /// <returns>a text string of the metadata if available or null</returns> private async Task <string> GetContentAsTextAsync() { string contents = null; ErrorMessage = null; if (string.IsNullOrEmpty(Path)) { ErrorMessage = "Metadata has no Path to the content."; return(null); } // using System.Diagnostics.Eventing.Reader; // Trace.TraceInformation("{0}: Start of Metadata.LoadASText({1})", DateTime.Now, Path); Stopwatch time = Stopwatch.StartNew(); // Try to guess an Undefined MetadataType if (Type == MetadataType.Undefined) { if (Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { Type = MetadataType.Url; } else if (await MyFile.ExistsAsync(Path)) { Type = MetadataType.FilePath; } else if (Path.Contains(".gdb\\", StringComparison.OrdinalIgnoreCase) || Path.Contains(".sde\\", StringComparison.OrdinalIgnoreCase) || Path.Contains(".mdb\\", StringComparison.OrdinalIgnoreCase)) { Type = MetadataType.EsriDataPath; } else { ErrorMessage = "The Type of the metadata Path is Undefined and not obvious."; return(null); } } // Correct a URL with the file:// scheme if (Type == MetadataType.Url && new Uri(Path).IsFile) { Path = new Uri(Path).LocalPath; Type = MetadataType.FilePath; } // Try and load the content try { if (Type == MetadataType.FilePath) { contents = await MyFile.ReadAllTextAsync(Path); } if (Type == MetadataType.EsriDataPath) { contents = await GisInterface.GetMetadataAsXmlAsync(Path); Format = MetadataFormat.Xml; } if (Type == MetadataType.Url) { // If the format is Undefined, I need to download the contents to check the Format // If the Format is Xml, I will download the contents for getting attributes // For Html and Text Formats, I can't do anything with the contents, so skip this step // The Display method will use the URL to get the contents if (Format == MetadataFormat.Xml || Format == MetadataFormat.Undefined) { var uri = new Uri(Path); ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; HttpClient client = new HttpClient(); var response = await client.GetAsync(uri); if (response.IsSuccessStatusCode) { contents = await response.Content.ReadAsStringAsync(); } else { ErrorMessage = $"Could not get {Path} from server. Status code: {response.StatusCode}({response.ReasonPhrase})"; return(null); } } } } catch (Exception ex) { ErrorMessage = ex.Message; Debug.Print("Exception thrown trying to load Metadata.\n" + ex); return(null); } // If the file, service, or geo-database returned nothing, we are done for. if (string.IsNullOrEmpty(contents) && (Type != MetadataType.Url || Format == MetadataFormat.Xml || Format == MetadataFormat.Undefined)) { ErrorMessage = "The content of the resource at Path is empty."; Format = MetadataFormat.Undefined; return(null); } // Test the format if (Format == MetadataFormat.Undefined) { // ReSharper disable once PossibleNullReferenceException if (contents.StartsWith("<?xml version=", StringComparison.OrdinalIgnoreCase) || contents.StartsWith("<metadata", StringComparison.OrdinalIgnoreCase)) { Format = MetadataFormat.Xml; } if (Regex.IsMatch(contents, "<html>|<html .+>")) { Format = MetadataFormat.Html; } if (!Regex.IsMatch(contents, "<.+>|</.+>")) { Format = MetadataFormat.Text; } } // time.Stop(); Trace.TraceInformation("{0}: End of Metadata.LoadASText() - Elapsed Time: {1}", DateTime.Now, time.Elapsed); return(contents); }
/// <summary> /// Creates a Metadata reference appropriate for the data source. /// </summary> /// <remarks> /// This method will guess the metadata Path, Type and Format based on Esri conventions /// and file existence. It is Async, because Iit will check the file system for a file /// and/or directory existance which will block, and could take significant time in unusual /// circumstances: missing drive, network connection problems, drive asleep, etc. This may /// need to check several different paths to ensure we do not miss existing metadata for a datasource. /// This method is only called by ThemeBuilder.cs when the user adds a new data source. /// </remarks> /// <param name="data">The theme's data source</param> /// <returns>a new Metadata object for a theme</returns> internal static async Task <Metadata> FromDataSourceAsync(ThemeData data) { Metadata newMetadata = new Metadata(); if (data == null) { return(newMetadata); } //Caution: Setting Path will clear the Type and Format, so set Path first. // General file based metadata // Includes layer files. if *.lyr.xml exists, it trumps the datasource metadata (for single datasource layers) if (data.Path != null && await MyFile.ExistsAsync(data.Path + ".xml")) { newMetadata.Path = data.Path + ".xml"; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; return(newMetadata); } // General file based metadata // Includes file based raster data, LAS datasets, and others. if (data.DataSource != null && await MyFile.ExistsAsync(data.DataSource + ".xml")) { newMetadata.Path = data.DataSource + ".xml"; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; return(newMetadata); } // Grids, TINs, and other directory based feature classes if (data.IsRasterBand && data.DataSource != null && await MyDirectory.ExistsAsync(data.DataSource)) { string metadataPath = System.IO.Path.Combine(data.DataSource, "metadata.xml"); if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } // Shapefile if (data.IsShapefile) { string metadataPath = data.DataSource + ".shp.xml"; if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } // ArcInfo Coverages if (data.IsCoverage && data.WorkspacePath != null && data.Container != null) { string coverageDir = System.IO.Path.Combine(data.WorkspacePath, data.Container); if (await MyDirectory.ExistsAsync(coverageDir)) { string metadataPath = System.IO.Path.Combine(coverageDir, "metadata.xml"); if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } } // CAD if (data.IsCad && data.WorkspacePath != null && data.Container != null) { string cadFile = System.IO.Path.Combine(data.WorkspacePath, data.Container); if (await MyFile.ExistsAsync(cadFile)) { string metadataPath = cadFile + ".xml"; if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } } // SDC - Smart Data Compression for ESRI Street Map and Sample datasets. if (data.IsSdc && data.WorkspacePath != null && data.Container != null) { string sdcFile = System.IO.Path.Combine(data.WorkspacePath, data.Container); if (await MyFile.ExistsAsync(sdcFile)) { string metadataPath = sdcFile + ".xml"; if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } } // GeoDatabases - Metadata is not a separate XML file, it is internal to the database // For SDE datasets to work, the original connection file (*.sde) must be available // to all theme manager users, i.e. not in a local profile (the typical default location) if (data.IsInGeodatabase) { newMetadata.Path = data.DataSource; if (data.IsRasterBand) { newMetadata.Path = data.DataSource?.Replace("\\" + data.DataSourceName, ""); } newMetadata.Type = MetadataType.EsriDataPath; newMetadata.Format = MetadataFormat.Xml; return(newMetadata); } // File based Raster band metadata // DataSource will contain the Band Name, while metadata will not // Needs to be done after geodatabase, else we miss rasterbands in geodatabases if (data.IsRasterBand && data.WorkspacePath != null && data.Container != null) { string rasterName = System.IO.Path.Combine(data.WorkspacePath, data.Container); string metadataPath = rasterName + ".xml"; if (await MyFile.ExistsAsync(metadataPath)) { newMetadata.Path = metadataPath; newMetadata.Type = MetadataType.FilePath; newMetadata.Format = MetadataFormat.Xml; } return(newMetadata); } // Esri Web services if ((data.IsEsriMapService || data.IsEsriImageService) && data.DataSource != null) { newMetadata.Path = Regex.Replace(data.DataSource, "/arcgis/services/", "/arcgis/rest/services/", RegexOptions.IgnoreCase); newMetadata.Path += "/info/metadata"; newMetadata.Type = MetadataType.Url; newMetadata.Format = MetadataFormat.Xml; return(newMetadata); } if (data.IsEsriFeatureService && data.WorkspacePath != null) { newMetadata.Path = Regex.Replace(data.WorkspacePath, "/arcgis/services/", "/arcgis/rest/services/", RegexOptions.IgnoreCase); newMetadata.Path += "/info/metadata"; newMetadata.Type = MetadataType.Url; newMetadata.Format = MetadataFormat.Xml; return(newMetadata); } // Other web services may have metadata, but we are not ready to support them // LiDAR data tiles (.las) are not data sources that ArcGIS manages directly (*.lasD files are handled above) // Raster functions have no permanent disk representation except a layer file // Do not provide a default, it was usually wrong, and it will be better to return nothing Debug.Print($"Metadata not found for Data.Path:{data.Path}, Data.DataSource:{data.DataSource}, Data.DataSetType:{data.DataSetType}"); return(newMetadata); }