scrollbar |
---|
...
Panel | ||||||
---|---|---|---|---|---|---|
| ||||||
Contents
|
Exercise outline
The goal of this exercise is to create and register a WaterML2 importer class. In At the end, it should be possible to import WaterML2 files (containing time series data) into a Delta Shell project.
Info |
---|
Information on WaterML2, a global standard for hydrological time series, can be found here: at http://www.waterml2.org/. |
Create a new importer class
Add a new folder to the plugin project a new folder named Importers. In this folder, create a new class named DemoAppApplicationPluginWaterML2TimeSeriesImporter.cs and add the following code:adapt the contents as shown below.
Note |
---|
In order to successfully build the code below, references need to be added to:
|
Code Block |
---|
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using DelftTools.Functions;
using DelftTools.Functions.Generic;
using DelftTools.Shell.Core;
using log4net;
namespace DeltaShell.Plugins.VolumeModel.Importers
{
/// <summary>
/// Importer for importing WaterML2 data to time series objects
|
Code Block |
using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Xml.Linq; using DelftTools.Functions; using DelftTools.Functions.Generic; using DelftTools.Shell.Core; using log4net; namespace DeltaShell.Plugin.DemoApp.Importers { public class WaterML2TimeSeriesImporter : IFileImporter { private static readonly ILog log = LogManager.GetLogger(typeof(WaterML2TimeSeriesImporter)); /// <summary></summary> public class WaterML2TimeSeriesImporter : ///IFileImporter The name of the importer{ /// </summary> public string Name private static readonly ILog Log = LogManager.GetLogger(typeof(WaterML2TimeSeriesImporter)); // Handle for writing log messages { /// <summary> /// getThe {name return "WaterML2 time series importer"; } of the importer } /// </summary> /// <summary> /// The category of the importer /// </summary><remarks>Used in importer selection dialogs</remarks> public string CategoryName { get { return "DemoApp importersWaterML2 time series importer"; } } /// <summary> /// The imagecategory of the importer /// </summary> public/// Bitmap<remarks>Used Image in importer selection dialogs</remarks> public string Category { get { return new Bitmap(16, 16);"Volume model importers"; } } /// <summary> /// The dataimage types supportedof by the importer /// </summary> public/// IEnumerable<Type><remarks>Used SupportedItemTypes in importer selection dialogs</remarks> { public Bitmap Image { get { yieldreturn returnnew typeof(TimeSeriesBitmap(16, 16); } } /// <summary> /// IndicatesThe data whethertypes orsupported notby the importer can import at root level (folder/project)./// </summary> /// If true, the importer will always show up in the project->import list. If public IEnumerable<Type> SupportedItemTypes { ///get false{ thisyield importer can only be retrieved by supported type, eg, in code.return typeof(TimeSeries); } } /// <summary> Use false for partial/composite importers and importers called from map tools /// etc.Indicates Ifthat true, the importer iscan assumedimport toat supportroot both "new" and "into" modes.level (folder/project). In other /// </summary> public bool CanImportOnRootLevelwords, indicates that the <see cref="ImportItem"/> method can be called without { /// specifying a time series target... get { return true; } /// </summary> public bool }CanImportOnRootLevel /// <summary>{ get { return true; } } /// <summary> /// The file filter of the importer /// </summary> /// <remarks>Used <example>"Myin file format1 (*.ext1)|*.ext1|My file format2 (*.ext2)|*.ext2"</example>selection dialogs</remarks> public string FileFilter { get { return "WaterML2 files|*.XML"; } } /// <summary> /// Path where external data files can be copied into /// </summary> /// <remarks>Optional,<remarks>Not usedrelevant onlyin when external files need to be copied into the project "as is"</remarks> public string TargetDataDirectory { this tutorial</remarks> public string TargetDataDirectory { get; set; } /// <summary> /// Whether or not an import task should be cancelled /// </summary> /// <remarks>This<remarks>Not propertypart mustof be observed by the importer (thread-safe); when it is true the importer must stop current import task<this tutorial</remarks> public bool ShouldCancel { get; set; } /// <summary> /// Fired when progress has been changed /// </summary> /// <remarks>Not part in this tutorial</remarks> public ImportProgressChangedDelegate ProgressChanged { get; set; } /// <summary> /// Imports WaterML2 data from the file with path <paramref name="path"/> to target the /// time series <paramref name="target"/> /// </summary> /// <remarks> /// <remarks>TheThe target parameter is optional. If a target istime series is specified, the /// importer should import newthe WaterML2 data to this intoexisting thetime targetseries. IfWhen // no target is specifiedset, the importer should import the data intocreate a new object.</remarks>time series. /// </remarks> public object ImportItem(string path, object target = null) { // Check the file path if (!File.Exists(path)) { logLog.Error("File does not exist"); return null; } // Obtain a new time series or check the provided target for being a time series var timeSeries = target == null ? new TimeSeries { Name = Path.GetFileNameWithoutExtension(path), Components = { new Variable<double>() } } : target as TimeSeries; if (timeSeries == null) { logLog.Error("Target is of the wrong type (should be time series)"); return null; } // Load the WaterML2 XML document var doc = XDocument.Load(path); // Obtain the document elements var xElements = doc.Descendants(); // Obtain the measurement TVP tags var measurements = xElements.Where(element => element.Name.LocalName == "MeasurementTVP"); // Get the corresponding time and value for each measurement tag foreach (var measurement in measurements) { var time = DateTime.Parse(measurement.Elements().First(e => e.Name.LocalName == "time").Value); var value = double.Parse(measurement.Elements().First(e => e.Name.LocalName == "value").Value); timeSeries[time] = value; } // Return the time series return timeSeries; } } } |
Info |
---|
Fixme: Info on the code above.
...
The importer class is derived from the IFileImporter interface so that it can be registered in the application plugin |
...
Wiki Markup |
---|
*\[TODO\]* Fixme |
Exercise results
Wiki Markup |
---|
*\[TODO\]* Description of the exercise results
\\
\\
\\
\\
\\ |
1. Add a new folder to the project named "Importers"
2. Create a new class named "WaterML2TimeSeriesImporter"
3. Add the following contents to this class:
Code Block |
---|
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using DelftTools.Functions;
using DelftTools.Functions.Generic;
using DelftTools.Shell.Core;
using log4net;
namespace DeltaShell.Plugin.DemoApp.Importers
{
public class WaterML2TimeSeriesImporter : IFileImporter
{
private static readonly ILog log = LogManager.GetLogger(typeof(WaterML2TimeSeriesImporter));
public string Name
{
get { return "WaterML2 time series importer"; }
}
public string Category
{
get { return "DemoApp importers"; }
}
public Bitmap Image
{
get { return new Bitmap(16, 16);}
}
public IEnumerable<Type> SupportedItemTypes
{
get { yield return typeof(TimeSeries); }
}
public bool CanImportOnRootLevel
{
get { return true; }
}
public string FileFilter
{
get { return "WaterML2 files|*.XML"; }
}
public string TargetDataDirectory { get; set; }
public bool ShouldCancel { get; set; }
public ImportProgressChangedDelegate ProgressChanged { get; set; }
public object ImportItem(string path, object target = null)
{
// Check the file path
if (!File.Exists(path))
{
log.Error("File does not exist");
return null;
}
// Obtain a new time series or check the provided target for being a time series
var timeSeries = target == null
? new TimeSeries { Name = Path.GetFileNameWithoutExtension(path), Components = { new Variable<double>() } }
: target as TimeSeries;
if (timeSeries == null)
{
log.Error("Target is of the wrong type (should be time series)");
return null;
}
// Load the XML document
var doc = XDocument.Load(path);
// Obtain the document elements
var xElements = doc.Descendants();
// Obtain the measurement TVP tags
var measurements = xElements.Where(element => element.Name.LocalName == "MeasurementTVP");
// Get the corresponding time and value for each measurement tag
foreach (var measurement in measurements)
{
var time = DateTime.Parse(measurement.Elements().First(e => e.Name.LocalName == "time").Value);
var value = double.Parse(measurement.Elements().First(e => e.Name.LocalName == "value").Value);
timeSeries[time] = value;
}
// Return the time series
return timeSeries;
}
}
}
|
4. Register the importer in the application plugin class by adding the following code to DemoAppApplicationPlugin:
Code Block |
---|
public override IEnumerable<IFileImporter> GetFileImporters()
{
yield return new WaterML2TimeSeriesImporter();
}
|
Wiki Markup |
---|
5. Run the application, right click on Project and click Import. Select the newly implemented importer and press OK. \[TODO: Image\] |
...
(see the next step). The comments in the code explain the different parts of the importer implementation. |
Register the importer in the application plugin class
Register the importer in the application plugin by adding the following code to VolumeModelApplicationPlugin.cs:
Code Block |
---|
using System.Collections.Generic;
using DeltaShell.Plugins.VolumeModel.Importers;
|
and
Code Block |
---|
public override IEnumerable<IFileImporter> GetFileImporters()
{
yield return new WaterML2TimeSeriesImporter();
}
|
Delta Shell should now be able to find the importer when importing data on new or existing time series objects.
Exercise results
First of all, download the following WaterML2 XML: WaterML2_precipitation_data.XML.
Then, run the application and start importing a new project item (right click on project | Import...). Make sure that the new importer is selected in the dialog:
If you next click on OK, a file selection dialog pops up. Select the previously downloaded WaterML2 XML file and continue with the wizard.
After finishing the import, a new time series item, containing the data as shown in the following image, should be added to the project (double click the precipitation item in the Project window):
In the steps above, a project level import has been performed which creates a completely new time series item. The importer, however, is also able to import WaterML2 data into existing time series. Although this feature will be further used in some of the upcoming exercises, it can already be tested by sequentially:
- clearing (a part of) the imported time series data via the table view;
- importing the downloaded WaterML2 XML file directly onto this just modified time series item (right click on the time series item | Import ...).
scrollbar |
---|
...